2016-12-03 61 views
2

我在一本書中讀到說,由於編譯器優化,代碼執行可能會被重新排序,導致ReaderThread無限循環。這怎麼可能?Java多線程可見性?

public class NoVisibility { 
private static boolean ready; 
private static int number; 
private static class ReaderThread extends Thread { 
    public void run() { 
     while (!ready) 
      Thread.yield(); 
     System.out.println(number); 
     } 
    } 
    public static void main(String[] args) { 
     new ReaderThread().start(); 
     number = 42; 
     ready = true; 
    } 
} 
+0

你試過運行上面的代碼嗎?當我使用JRE-8 – AADProgramming

+0

@AADTechnical在Eclipse中運行它時,它並沒有進入無限循環 - 這沒有任何證據。底線是JLS *允許*代碼導致無限循環。看到我的答案。 –

+0

@AADTechnical是的,我用5個ReaderThreads在我的臺式電腦上運行,它們完美地執行(打印42)。但這取決於編譯器設置和CPU我想。這是「Java併發實踐」一書中的一個例子。 – sonnywang

回答

4

這怎麼可能?

代碼重新排序是可能的(一般),因爲Java語言規範(JLS)說,這是可能的。但是,重新排序(可能)不會成爲這裏的問題。相反,無限循環很可能是由硬件內存緩存行爲引起的。

在這種情況下,有沒有在JLS是需要的寫入由main方法制得的變量是子線程可見。技術上的解釋是,沒有發生在鏈之前,將寫入鏈接到(後續)讀取。如果沒有關鍵的發生在鏈之前,則無法保證可見性。

請注意,這裏是否實際存在無限循環將取決於各種因素。重點在於,考慮到示例代碼的寫入方式,這是一種可能性。

+0

謝謝你,斯蒂芬。那麼這是否意味着子線程會檢查變量「ready」的副本或緩存,以便主線程的更改不可見?否則它不可能的子線程將導致無限循環。只是好奇。 – sonnywang

+0

這是一種可能性。另一個是主線程不刷新寫入。沒有其他理論上的理由可以解釋其他硬件架構。 –

+0

理解這一點的正確方法是根據H-B關係進行思考。緩存刷新/內存屏障/無論是在某些硬件上執行程序中實現所需行爲的機制。 –

-1

它可能是由CPU緩存引起的。 ReaderThread可能會看到「ready」變量的過時值,因此它可能不會觸發while循環。過時的「就緒」值被稱爲陳舊數據。

+0

爲什麼要投票?這是用Java Concurrency in Action書中所寫的解釋。 – sonnywang