多線程 happens-before規(guī)則

簡介

happen before 在之前發(fā)生
Java使用新的JSR-133內(nèi)存模型,JSR-133使用happens-before的概念來闡述操作之間的內(nèi)存可見性。在JMM中,如果一個操作執(zhí)行的結(jié)果需要對另一個操作可見,那么這兩個操作之間必須要存在happens-before關(guān)系。這里提到的兩個操作既可以是在一個線程之內(nèi),也可以是在不同線程之間

規(guī)則

  1. 程序順序規(guī)則:在一個單獨(dú)的線程中,按照程序代碼的執(zhí)行流順序,(時間上)先執(zhí)行的操作happen—before(時間上)后執(zhí)行的操作。
  2. 管理鎖定規(guī)則:一個unlock操作happen—before后面(時間上的先后順序,下同)對同一個鎖的lock操作。
  3. volatile變量規(guī)則:對一個volatile變量的寫操作happen—before后面對該變量的讀操作。
  4. 線程啟動規(guī)則:Thread對象的start()方法happen—before此線程的每一個動作。
  5. 線程終止規(guī)則:線程的所有操作都happen—before對此線程的終止檢測,可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值等手段檢測到線程已經(jīng)終止執(zhí)行。
  6. 線程中斷規(guī)則:對線程interrupt()方法的調(diào)用happen—before發(fā)生于被中斷線程的代碼檢測到中斷時事件的發(fā)生。
  7. 對象終結(jié)規(guī)則:一個對象的初始化完成(構(gòu)造函數(shù)執(zhí)行結(jié)束)happen—before它的finalize()方法的開始。
  8. 傳遞性:如果操作A happen—before操作B,操作B happen—before操作C,那么可以得出A happen—before操作C。
    需要注意的是
    兩個操作之間具有happens-before關(guān)系,并不意味著前一個操作必須要在后一個操作之前執(zhí)行!happens-before僅僅要求前一個操作(執(zhí)行的結(jié)果)對后一個操作可見,且前一個操作按順序排在第二個操作之前。

對于java程序員來說,happens-before規(guī)則簡單易懂,它避免java程序員為了理解JMM提供的內(nèi)存可見性保證而去學(xué)習(xí)復(fù)雜的重排序規(guī)則以及這些規(guī)則的具體實(shí)現(xiàn)。

happens-before的定義

happens-before的概念最初由Leslie Lamport在其一篇影響深遠(yuǎn)的論文(《Time,Clocks and the Ordering of Events in a Distributed System》)中提出。Leslie Lamport使用happens-before來定義分布式系統(tǒng)中事件之間的偏序關(guān)系(partial ordering)。Leslie Lamport在這篇論文中給出了一個分布式算法,該算法可以將該偏序關(guān)系擴(kuò)展為某種全序關(guān)系。

全序是指,集合中的任兩個元素之間都可以比較的關(guān)系。比如實(shí)數(shù)中的任兩個數(shù)都可以比較大小,那么“大小”就是實(shí)數(shù)集的一個全序關(guān)系。
偏序是指,集合中只有部分元素之間可以比較的關(guān)系。比如復(fù)數(shù)集中并不是所有的數(shù)都可以比較大小,那么“大小”就是復(fù)數(shù)集的一個偏序關(guān)系。顯然,全序關(guān)系必是偏序關(guān)系。反之不成立。

JSR-133使用happens-before的概念來指定兩個操作之間的執(zhí)行順序。由于這兩個操作可以在一個線程之內(nèi),也可以是在不同線程之間。因此,JMM可以通過happens-before關(guān)系向程序員提供跨線程的內(nèi)存可見性保證(如果A線程的寫操作a與B線程的讀操作b之間存在happens-before關(guān)系,盡管a操作和b操作在不同的線程中執(zhí)行,但JMM向程序員保證a操作將對b操作可見)。

《JSR-133:Java Memory Model and Thread Specification》對happens-before關(guān)系的定義如下。
1)如果一個操作happens-before另一個操作,那么第一個操作的執(zhí)行結(jié)果將對第二個操作可見,而且第一個操作的執(zhí)行順序排在第二個操作之前。
2)兩個操作之間存在happens-before關(guān)系,并不意味著Java平臺的具體實(shí)現(xiàn)必須要按照happens-before關(guān)系指定的順序來執(zhí)行。如果重排序之后的執(zhí)行結(jié)果,與按happens-before關(guān)系來執(zhí)行的結(jié)果一致,那么這種重排序并不非法(也就是說,JMM允許這種重排序)。

上面的1)是JMM對程序員的承諾。從程序員的角度來說,可以這樣理解happens-before關(guān)系:如果A happens-before B,那么Java內(nèi)存模型將向程序員保證——A操作的結(jié)果將對B可見,且A的執(zhí)行順序排在B之前。注意,這只是Java內(nèi)存模型向程序員做出的保證!
上面的2)是JMM對編譯器和處理器重排序的約束原則。正如前面所言,JMM其實(shí)是在遵循一個基本原則:只要不改變程序的執(zhí)行結(jié)果(指的是單線程程序和正確同步的多線程程序),編譯器和處理器怎么優(yōu)化都行。JMM這么做的原因是:程序員對于這兩個操作是否真的被重排序并不關(guān)心,程序員關(guān)心的是程序執(zhí)行時的語義不能被改變(即執(zhí)行結(jié)果不能被改變)。因此,happens-before關(guān)系本質(zhì)上和as-if-serial語義是一回事。

密切相關(guān)規(guī)則

與程序員密切相關(guān)的happens-before規(guī)則如下:

程序順序規(guī)則:一個線程中的每個操作,happens- before 于該線程中的任意后續(xù)操作。
監(jiān)視器鎖規(guī)則:對一個監(jiān)視器鎖的解鎖,happens- before 于隨后對這個監(jiān)視器鎖的加鎖。
volatile變量規(guī)則:對一個volatile域的寫,happens- before 于任意后續(xù)對這個volatile域的讀。
傳遞性:如果A happens- before B,且B happens- before C,那么A happens- before C。
注意,兩個操作之間具有happens-before關(guān)系,并不意味著前一個操作必須要在后一個操作之前執(zhí)行!happens-before僅僅要求前一個操作(執(zhí)行的結(jié)果)對后一個操作可見,且前一個操作按順序排在第二個操作之前(the first is visible to and ordered before the second)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容