java記憶體模型中的先行發生原則
先行發生原則
前言
由上一篇,我們知道併發問題的一個原因是有序性,而java中volatile和synchronized可以保證有序性;
但是在java中,並不是所有的操作都是由volatile和synchronized實現的,java中存在”先行發生“的原則。
“先行發生”原則是判斷資料是否存在競爭、執行緒是否安全的主要依據;
什麼是先行發生原則?
先行發生原則是指:如果說操作A先行發生於操作B,也就是發生在操作B之前,操作A產生的影響能被操作B觀察到。
舉例說明:
//以下操作線上程A中執行
i = 1;
//以下操作線上程B中執行
j = i;
//以下操作線上程C中執行
i = 2;
假設執行緒A中的操作i=1先行發生於執行緒B的操作j=i,那麼可以確定線上程B的操作執行之後,j一定等於1。因為:根據先行發生原則,i=1的結果可以被B觀察到;
現在保持A先行發生於B,執行緒C出現在A與B之間,但是執行緒C與B沒有先行發生關係。那麼j會等於多少呢?答案至不確定。因為執行緒C對變數i的影響可能會被B觀察到,也可能不會。因為兩者之間沒有先行發生關係;
====》先行發生原則就是操作A在時間上或者邏輯上比B先發生,那麼B一定能看到A操作帶來的影響(修改了共享變數的值等等),那麼此時A就是先行發生於B。
你可能會說難道B還有可能不會看到A帶來的影響嗎?A操作先執行的呀!想一想我們上面提到的記憶體可見性和有序性…
java記憶體模型中的”天然“先行關係
1、程式次序規則
一個執行緒內,按照程式程式碼的順序,書寫在前面的操作先行發生於(邏輯上)書寫在後面的操作。
2、管程鎖定規則
一個unlock操作先行發生於後面對同一個鎖的lock操作。後面指時間上的先後順序。
3、volatile變數規則
對一個volatile變數的寫操作先行發生於後面對這個變數的讀操作。這裡的後面指時間上的先後順序。
4、傳遞性
如果操作A先行發生於操作B,操作B先行發生於操作C,那麼,操作A也就先行發生於操作C。
5、執行緒啟動規則
Thread物件的start方法先行發生於此執行緒的每個動作;
6、執行緒終止規則
執行緒中的所有操作都先行發生於對此執行緒的終止檢測;
7、執行緒中斷規則
對執行緒的interrupt()方法的呼叫先行發生於被中斷執行緒的程式碼檢測到中斷時間的發生;
8、物件終結規則
一個物件的初始化完成先行發生於它的finalize方法的開始;
。。。。。
java無須任何手段即可保證上面的先行發生規則成立;
分析
先行發生原則(happens-before)不是描述實際操作的先後順序,它是用來描述可見性的一種規則;
對上述中原則的理解:
對2)管程鎖定規則的理解:如果執行緒1解鎖了a,接著執行緒2鎖定了a,那麼,執行緒1解鎖a之前的寫操作都對執行緒2可見(執行緒1和執行緒2可以是同一個執行緒)。
對3)volatile變數規則的理解:如果執行緒1寫入了volatile變數v(這裡和後續的“變數”都指的是物件的欄位、類欄位和陣列元素),接著執行緒2讀取了v,那麼,執行緒1寫入v及之前的寫操作都對執行緒2可見(執行緒1和執行緒2可以是同一個執行緒)。
總結
先行發生原則的作用:判斷記憶體可見性與重排序是否造成併發問題。