Java併發中volatile和happen before是什麼? - javarevisited

banq 發表於 2020-06-30

要了解happen before,需要首先了解如果多個執行緒訪問同一個變數會發生什麼問題?尤其是當一個執行緒寫入該變數,而一個執行緒同時從該變數讀取時。

例如,假設我們有以下由執行緒T1執行的程式碼(請注意,整數變數y在x之前初始化):

int y = 1;
int x = 2;

現在,我們還有另一段程式碼T2,它由另一個執行緒T2執行,在該執行緒中,兩個變數的值都被列印出來(注意變數x在y之前列印):

System.out.print(x);
System.out.println(y);

螢幕上應該列印什麼?如果此程式碼是由同一執行緒執行的,則可以保證將2和1列印出來,但是對於多個執行緒,則沒有保證的行為。T2很有可能看不到T1所做的分配,而只為x和y輸出0。 也可能只看到x或y的初始化並進行相應列印。 這是可怕的且不可預測的,想想如果X是您銀行帳戶中的錢,您肯定想要可預測的行為。那就是happen before需要做的事情

happen before提供了某種順序和可見性保證:

[b]-“一個[/b]
volatile變數的
[b]寫入將在另一個對該[/b]
volatile變數
[b]讀取之前發生。”[/b]

-“同步塊上的解鎖將在另一個對其上鎖之前發生。”

執行緒T1寫入volatile變數的值也就是所有更改將線上程T2讀取相同volatile變數時可見,執行緒T1解鎖之前,對T2在同一鎖監視器內的鎖進行上鎖之前也是可見的。(這樣通過前後關係保證了T1和T2兩個執行緒操作同一個資源的先後順序)

落實到前面程式碼,修改如下:

T1:
int y = 1;
<p>[b]volatile [/b]int x = 2;  

T2:
System.out.print(x);
System.out.println(y);

增加了volatile 變數修飾之後,建立了兩個執行緒的happen before先後順序。

這時,T2肯定是輸出了x=2的結果,因為它見到了T1的所有更改結果,這是volatile 作用,那麼結果是

2和1或2和0?

如果您在T1進行volatile變數寫入而T2進行volatile變數讀取之前應用了happens-before這樣關係,那麼它還將看到y = 1的值,雖然沒有使用volatile修飾符。

最後結果是確定的2和1。

這裡要記住的關鍵是,執行緒T2甚至可以看到非易失性volatile變數的值。這裡只是發生happens-before關係的一個示例,但在分析多執行緒程式的行為時非常有用。