聲明
本文轉自volatile 變量使用條件
參考網頁
https://www.ibm.com/developerworks/cn/java/j-jtp06197.html
使用volatile的條件
volatile 變量可以被看作是一種 “程度較輕的 synchronized”;與 synchronized 塊相比,volatile 變量所需的編碼較少,並且運行時開銷也較少,但是它所能實現的功能也僅是 synchronized 的一部分。
只能在有限的一些情形下使用 volatile 變量替代鎖。要使 volatile 變量提供理想的線程安全,必須同時滿足下面兩個條件:
對變量的寫操作不依賴於當前值。
該變量沒有包含在具有其他變量的不變式中。
實際上,這些條件表明,可以被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。
對變量的寫操作不依賴於當前值--啥意思?
就是如果變量a定義為volatile變量,做諸如
a++;
a = a + 2;
a = 6 * a;
a = a * a;
這些操作,在多線程場景下會出現共享變量不一致的情形。
原因就是volatile無法保證原子性。如果指令執行到中間被打斷,就會出現共享變量不一致的情形。
例子
可以見
https://my.oschina.net/u/3866531/blog/1936097
其中的Test.java,int類型的變量inc被volatile修飾(inc為共享變量),但是多線程場景下inc的自增操作出現了問題。原因很簡單,volatile保證不了操作的原子性。
進一步說明
這個條件的限制使 volatile 變量不能用作線程安全計數器。雖然增量操作(x++)看上去類似一個單獨操作,實際上它是一個由讀取-修改-寫入操作序列組成的組合操作,必須以原子方式執行,而 volatile 不能提供必須的原子特性。實現正確的操作需要使 x 的值在操作期間保持不變,而 volatile 變量無法實現這點。(然而,如果將值調整為只從單個線程寫入,那么可以忽略第一個條件。)
該變量沒有包含在具有其他變量的不變式中--啥意思?
例子代碼
@NotThreadSafe
public class NumberRange { private int lower, upper; public int getLower() { return lower; } public int getUpper() { return upper; } public void setLower(int value) { if (value > upper) throw new IllegalArgumentException(...); lower = value; } public void setUpper(int value) { if (value < lower) throw new IllegalArgumentException(...); upper = value; } }
代碼解釋
代碼顯示了一個非線程安全的數值范圍類。它包含了一個不變式 —— 下界總是小於或等於上界。
這種方式限制了范圍的狀態變量,因此將 lower 和 upper 字段定義為 volatile 類型不能夠充分實現類的線程安全;從而仍然需要使用同步。否則,如果湊巧兩個線程在同一時間使用不一致的值執行 setLower 和 setUpper 的話,則會使范圍處於不一致的狀態。例如,如果初始狀態是 (0, 5),同一時間內,線程 A 調用 setLower(4) 並且線程 B 調用 setUpper(3),顯然這兩個操作交叉存入的值是不符合條件的,那么兩個線程都會通過用於保護不變式的檢查,使得最后的范圍值是 (4, 3) —— 一個無效值。至於針對范圍的其他操作,我們需要使 setLower() 和 setUpper() 操作原子化 —— 而將字段定義為 volatile 類型是無法實現這一目的的。