2012-10-21 72 views
2

的Java併發教程忽略原子聲明指出:沒有volatile關鍵字

讀取和寫入原子參考變量和最原始的變量(所有類型除了長和雙)。
對於聲明爲volatile的所有變量(包括長變量和雙變量),讀取和寫入是原子的。

我創建了一個簡單的實驗來測試這個

package experiment0; 

public class Experiment0 { 

    public static int i=9; 

    public static void main(String[] args) { 
     i=9; 
     (new Thread(){ 
      public void run(){ 
       while(i==9){ 
        //System.out.println("i==9"); 
       } 
       System.out.println("\ni!=9"); 
      } 
     }).start(); 
     (new Thread(){ 
      public void run(){ 
       try{ 
        Thread.sleep(3000); 
        i=8; 
       }catch(Exception e){ 

       } 
      } 
     }).start(); 
    } 
} 

當我運行這段代碼的程序永遠不會被終止!爲了使其工作,我必須在第一個線程的while循環內取消註釋行,或者將i聲明爲volatile。 我錯過了什麼嗎?文件是否錯誤,是否應該說在這種情況下所有原始圖像都應該被宣佈爲揮發性物質?爲什麼寫一個System.out「解決」這個問題?它是否給第二條線足夠的時間來改變i

+0

在我的情況下,兩者都能正常工作。 –

+2

您引用的手冊部分和您的代碼存在的問題並沒有真正相關。 「原子」寫入並不意味着另一個線程會看到這些更改。你可以做的更詳細的探索之一是看看編譯器是否優化了讀取,這就是爲什麼你沒有看到更新。 – Jochen

+2

原子意味着更新是不可分割的。你永遠不會看到中間狀態,只有舊的或新的價值。如果你只看到舊的價值,它仍然是原子的。 –

回答

3

寫入爲原子意味着寫入作爲單個原子操作執行。沒有其他線程可以看到除賦值之前的值或賦值之後的值。

例如,在長值的情況下,賦值不是原子的,因爲它實際上是作爲兩個操作執行的:一個賦值前32位,一個賦值最後32位。因此,另一個線程可能在兩個操作中的一個被執行後讀取該變量的值,從而查看既不是前值也不是後值的值,而是「中間值」。

你的程序演示的是寫入一個沒有任何同步的變量導致可見性線程之間的問題。一個線程完成的寫入操作不會被另一個線程看到。這是由於處理器每個都有自己的緩存,並且不會在每次分配時寫入主內存。爲了確保寫入是可見的,您需要使變量volatile,或者同步對變量的訪問,或者使用AtomicInteger。

+0

哦,我明白了,謝謝!我以前從來沒有聽說過這個可見性概念。 – Bat0u89

+1

@JB Nizet:現代處理器不會在硬件中實現Cache一致性嗎?所以當'i = 8'在一個處理器中執行,然後由另一個處理器讀取時,硬件會刷新緩存並從內存中讀取。 –

+0

@ vikky.rk是的。然而,Java內存模型並不要求體系結構是緩存一致的,並且允許Java虛擬機優化循環'while(i == 9){}'到'if(i == 9){while (true){}}'。由於這種優化,您仍然可以在高速緩存一致體系結構上遇到線程可見性問題。 – Boann