簡體   English   中英

while循環和隊列的奇怪java行為

[英]Strange java behavior with while loop and queue

我有while(true)循環的奇怪行為。 這是代碼:

作為班級成員,我有:

static Queue<Object> response = new LinkedList<Object>();

......和一個功能:

private void read() {

    while (true)
    {
        System.out.println("foo");
        if(!(response.isEmpty()))
        {

            if((Boolean)response.peek() == true)
            {
                view.dispose();
                LogInControler controler= new LogInControler();
                disableMasterLogin();
                response.poll();
                return;
            }
            else if((Boolean)response.poll() == false)
            {
                JOptionPane.showMessageDialog(view.getRootPane(), 
                        "Wrong username or password.");
                view.tfUsername.requestFocus();
                return;
            }
        }
    }
}

當從服務器接收對象(通過Socket)時,InputController類將該對象傳遞給適當的控制器,在本例中為MasterLogInController並將其放入Queue響應中。 我在while(true)循環中等待響應,但問題是如果我刪除“System.out.printline(”foo“);” 循環只會輸入一次!? 使用這個syso行,我“強制”while循環執行循環,直到收到響應。 這有什么不對?

我假設你有幾個線程在運行。

System.out.println創建了一個內存屏障,可能會幫助您的代碼看到一些不可見的變量(因為缺少同步)。

特別是,您的隊列不是線程安全的,似乎安全發布。 因此可以想象:

  • 你的while循環可能會看到response為null ==> NullPointerException
  • reponse.isEmpty()可能返回false,但是response.peek()可能會返回null,然后在你的條件下轉換為Boolean和unbox if((Boolean)xxx == true) ==> NullPointerException
  • 等等

除了評論中給出的有助於理解原因的合理建議之外,您還應該使代碼線程安全。 例如,您可以使用線程安全的BlockingQueue 但這可能還不夠(因為你的各種if / if / else if語句的布局方式以及隊列可能被每個語句之間的另一個線程更改的事實)。

我懷疑正在發生的事情是你的循環被JIT編譯器優化了。 如果第一次在循環中調用response.isEmpty()為true,並注意到response不在synchronized塊或方法中,或者標記為volatile ,則JIT編譯器可能會認為它不會更改並且從運行代碼中刪除看似空忙的循環。

在JIT編譯器的眼中,添加println()語句至少給出了循環的目的,因此在這種情況下它將保持運行。

要解決這個問題,除了assylias給出的顯着建議之外,你可以將所有對response引用放在synchronized塊中,如下所示:

public void read() {
    Boolean result = null;
    synchronized (response) {
        while (true) {
            result = (Boolean) response.poll();
            if (result != null) break;
            try {
                response.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
                // You could put return; here
            }
        }
    }
    // result should always be non null here
    if (result) {
         view.dispose();
         LogInControler controler = new LogInControler();
         disableMasterLogin();
    } else {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                JOptionPane.showMessageDialog(view.getRootPane(), "Wrong username or password");
                view.tfUsername.requestFocus();
            }
        });
    }
}

如果您的其他線程正在將響應添加到隊列,請確保它也在同步塊中並調用notifyAll()

public void addResult(Object result) {
    synchronized (response) {
        response.add(result);
        response.notifyAll();
    }       
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM