volatile 變量使用條件

參考網頁

https://www.ibm.com/developerworks/cn/java/j-jtp06197.htmlhtml

使用volatile的條件

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 類型是沒法實現這一目的的。

通俗的說--多線程場景下,【變量作依賴於本身的值的操做】,【變量包含在另外一個變量的不變式中】,這兩種狀況下即便使用volatile修飾變量,同步操做仍然會出現問題

相關文章
相關標籤/搜索