https://www.ibm.com/developerworks/cn/java/j-jtp06197.htmlhtml
volatile 變量能夠被看做是一種 「程度較輕的 synchronized」;與 synchronized 塊相比,volatile 變量所需的編碼較少,而且運行時開銷也較少,可是它所能實現的功能也僅是 synchronized 的一部分。java
只能在有限的一些情形下使用 volatile 變量替代鎖。要使 volatile 變量提供理想的線程安全,必須同時知足下面兩個條件:安全
對變量的寫操做不依賴於當前值。多線程
該變量沒有包含在具備其餘變量的不變式中。編碼
實際上,這些條件代表,能夠被寫入 volatile 變量的這些有效值獨立於任何程序的狀態,包括變量的當前狀態。spa
就是若是變量a定義爲volatile變量,作諸如.net
a++;線程
a = a + 2;code
a = 6 * a;htm
a = a * a;
這些操做,在多線程場景下會出現共享變量不一致的情形。
緣由就是volatile沒法保證原子性。若是指令執行到中間被打斷,就會出現共享變量不一致的情形。
能夠見
http://www.javashuo.com/article/p-glkohnan-dq.html
其中的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 類型是沒法實現這一目的的。