2012-12-09 34 views
5

while(true)loop有奇怪的行爲。下面是代碼:while循環和隊列的奇怪java行爲

作爲類的成員我有:

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; 
      } 
     } 
    } 
} 

當從服務器(通過插座)接收到的對象,InputController類將該對象傳遞給適當的控制器,在這種情況下爲MasterLogInController並將其放入Queue響應中。 我正在等待while(true)循環中的響應,但問題是如果我刪除「System.out.printline(」foo「);」循環只會輸入一次!?有了這個系統行我「強制」while循環做循環,直到收到響應。這裏有什麼問題?

+0

你只要把真正的while循環,你需要指定什麼必須是真實的。 – DrinkJavaCodeJava

+0

對我而言,這聽起來像是某種競爭條件。你在開始的線程中調用它,對吧?嘗試捕獲此方法中的所有例外並打印它們。 –

+0

查看https://stackoverflow.com/questions/25425130/loop-doesnt-see-changed-value-without-a-print-statement – Boann

回答

4

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

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

特別是,您的隊列不是線程安全的,似乎是安全發佈的。所以這是非常可能的是:

  • while循環可能會看到response爲空==> NullPointerException異常
  • reponse.isEmpty()也可能返回false,但response.peek()可能返回null,然後您可以轉換爲Boolean和拆箱在你的病情if((Boolean)xxx == true) ==> NullPointerException異常

除了在評論中給出了中肯的意見,幫助理解的原因,你應該使代碼線程安全。例如,您可以使用thread safe BlockingQueue。但這可能還不夠(因爲你的各種if/if/else if語句的佈局以及這些語句之間的另一個線程可能會改變隊列的事實)。

+0

謝謝!這解釋了一切。 – Maleta

+0

只有改變我不得不使隊列我使用BlockingQueue response = new LinkedBlockingQueue ();但仍然很奇怪,爲什麼我有這個問題,而我的朋友在他的電腦上沒有(我們有相同版本的Java - 7u9)。 – Maleta

+0

它可以取決於許多因素,包括JVM參數(-client或-server),處理器體系結構,內核數量,操作系統(例如Windows與Linux),其他程序使用的CPU負載等等。一臺電腦只是巧合,不能依賴。如果您在朋友的電腦上多次運行該程序,它可能會在某個階段中斷。 – assylias

2

我懷疑發生了什麼事是你的循環被JIT編譯器優化不存在。如果response.isEmpty()第一次在您的循環中調用它,並且注意到response不在​​塊或方法內,或者標記爲volatile,那麼很可能JIT編譯器會決定它不會更改,只是刪除顯示的內容從運行代碼變成空閒的忙碌循環。

添加在println()語句中,至少給循環的目的,在JIT編譯器的眼中,所以它會讓它在這種情況下運行。

要解決這個問題,除了由assylias給出的突出的建議,你可以把所有引用response一個​​塊中,像這樣:

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(); 
      } 
     }); 
    } 
} 

如果您的其他線程也加入到隊列中的響應,確保也是一個synchronized塊,並呼籲notifyAll()

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

另外我在調用invokeLater時加入了'JOptionPane.showMessageDialog()'和'requestFocus()',因爲它們只應該在EDT上調用。 –