您好,登錄后才能下訂單哦!
這篇文章主要介紹“JMM中happens-before的原理和使用方法”,在日常操作中,相信很多人在JMM中happens-before的原理和使用方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”JMM中happens-before的原理和使用方法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
在JMM中有一個很重要的概念對于我們了解JMM有很大的幫助,那就是happens-before規則。happens-before規則非常重要,它是判斷數據是否存在競爭、線程是否安全的主要依據。JSR-133S使用happens-before概念闡述了兩個操作之間的內存可見性。在JMM中,如果一個操作的結果需要對另一個操作可見,那么這兩個操作則存在happens-before關系。
那什么是happens-before呢?在JSR-133中,happens-before關系定義如下:
如果一個操作happens-before另一個操作,那么意味著第一個操作的結果對第二個操作可見,而且第一個操作的執行順序將排在第二個操作的前面。
兩個操作之間存在happens-before關系,并不意味著Java平臺的具體實現必須按照happens-before關系指定的順序來執行。如果重排序之后的結果,與按照happens-before關系來執行的結果一致,那么這種重排序并不非法(也就是說,JMM允許這種重排序)
happens-before規則如下:
程序順序規則:一個線程中的每一個操作,happens-before于該線程中的任意后續操作。
監視器規則:對一個鎖的解鎖,happens-before于隨后對這個鎖的加鎖。
volatile規則:對一個volatile變量的寫,happens-before于任意后續對一個volatile變量的讀。
傳遞性:若果A happens-before B,B happens-before C,那么A happens-before C。
線程啟動規則:Thread對象的start()方法,happens-before于這個線程的任意后續操作。
線程終止規則:線程中的任意操作,happens-before于該線程的終止監測。我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值等手段檢測到線程已經終止執行。
線程中斷操作:對線程interrupt()方法的調用,happens-before于被中斷線程的代碼檢測到中斷事件的發生,可以通過Thread.interrupted()方法檢測到線程是否有中斷發生。
對象終結規則:一個對象的初始化完成,happens-before于這個對象的finalize()方法的開始。
以上8條happens-before規則都比較簡單,這里LZ只分析第3條volatile變量規則,分析如下:
從上圖中,我們看到存在4條happens-before關系,它們分別如下:
1 happens-before 2 和 3 happens-before 4 是有由程序順序性規則產生的。
2 happens-before 3 是由volatile規則產生的。上面提到過,一個volatile變量的讀,總能看到之前對這個volatile變量的寫入。
1 happens-before 4 是由傳遞性規則產生的。
讀到這里,可能很多童鞋會把happens-before理解為“時間上的先后順序”,在這里LZ特別強調happens-hefore不能理解為“時間上的先后順序”,下面LZ用一段代碼解釋寫happens-before和“時間上的先后順序”的不同,代碼如下:
public class VolatileTest4 { private int a = 0; public int getA() { return a; } public void setA(int a) { this.a = a; } }
上面代碼就是一組簡單的setter/getter方法,現在假設現在有兩個線程A和B,線程A先(這里指時間上的先執行)執行setA(10),然后線程B訪問同一個對象的getA()方法,那么此時線程B收到的返回值是對少呢?
答案是:不確定
我們來一次分析下happens-before的各項原則:
這里兩個方法分別是在兩個線程中被調用,不在一個線程中,這里程序順序性就不適用了
代碼中沒有同步快,所有監視器規則也不適用
代碼中變量a是一個普通變量,所以volatile規則也不適用
后面的線程啟動、中斷、終止和對象的終結和這里完全沒有關系,因此這些規則也是不適用的
沒有一條happens-before適用,因此傳遞性規則也不適用
在這里,雖然線程A在時間上先于線程B執行,但是由于代碼完全不適用happens-before規則,因此我們無法確定先B收到的值時多少。也就是說上面代碼是線程不安全的。
對于上面代碼,那我們如何修復線程不安全這個問題呢?這里,我們只要滿足happens-before規則中2、3的任意一種規則就可以了。即要么把setter/getter方法定義為synchronized方法,要么在變量a上加volatile修飾符。
通過上面的例子,我們可以得出結論:一個操作“時間上的先發生”不代表這個操作會happens-before其它操作。那一個操作happens-before其它操作,是否就表示這個操作是“時間上先發生”的呢?答案也是否定的,我們來看看下面一個示例:
int i = 1; int m = 2;
上面兩個賦值操作在同一個線程中,根據程序順序性規則,“int i = 1;"這個操作happens-before ”int m = 2;“這個操作,但是”int m = 2;“這個操作完全有可能被處理器先執行,這并不影響happens-before原則的正確性。因為這種重排序在JMM中是允許的。
最后我們得出的結論是:時間先后順序與happens-before原則之間基本沒有太大的關系,所以我們在衡量并發安全問題的時候不要受到時間順序的干擾,一切必須以happens-before原則為準。
到此,關于“JMM中happens-before的原理和使用方法”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。