happens-before原則
從JDK5開始,Java使用新的JSR-133記憶體模型,JSR-133使用happens-before的概念來闡述操作之間的記憶體可見性。在JMM中,如果一個操作執行的結果需要對另一個操作可見,那麼這兩個操作之間必須要存在happens-before關系。這裡提到的兩個操作既可以是在一個線程之内,也可以是在不同線程之間。
- 程式順序規則:一個線程中的每個操作,happens-before于該線程中的任意後續操作。
- volatile變量規則:對一個volatile域的寫,happens-before于任意後續對這個volatile域的讀。
- 螢幕鎖規則:對一個鎖的解鎖,happens-before于随後對這個鎖的加鎖。
- 傳遞性:如果A happens-before B,且B happens-before C,那麼A happens-before C。
- start()規則:如果線程A執行操作ThreadB.start()(啟動線程B),那麼A線程的ThreadB.start()操作happens-before于線程B中的任意操作。
- join()規則:如果線程A執行操作ThreadB.join()并成功傳回,那麼線程B中的任意操作happens-before于線程A從ThreadB.join()操作成功傳回。
- 線程的中斷(interrupt())先于被中斷線程的代碼
- 對象的構造函數執行、結束先于finalize()方法
一個happens-before規則對應于一個或多個編譯器和處理器重排序規則。對于Java程式員來說,happens-before規則簡單易懂,它避免Java程式員為了了解JMM提供的記憶體可見性保證而去學習複雜的重排序規則以及這些規則的具體實作方法。
《JSR-133:Java Memory Model and Thread Specification》對happens-before關系的定義如下。
1)如果一個操作happens-before另一個操作,那麼第一個操作的執行結果将對第二個操作可見,而且第一個操作的執行順序排在第二個操作之前。
2)兩個操作之間存在happens-before關系,并不意味着Java平台的具體實作必須要按照happens-before關系指定的順序來執行。如果重排序之後的執行結果,與按happens-before關系來執行的結果一緻,那麼這種重排序并不非法(也就是說,JMM允許這種重排序)
上面的1)是JMM對程式員的承諾 。從程式員的角度來說,可以這樣了解happens-before關系:如果A happens-before B,那麼Java記憶體模型将向程式員保證——A操作的結果将對B可見,且A的執行順序排在B之前。注意,這隻是Java記憶體模型向程式員做出的保證!
上面的2)是JMM對編譯器和處理器重排序的限制原則 。正如前面所言,JMM其實是在遵循一個基本原則:隻要不改變程式的執行結果(指的是單線程程式和正确同步的多線程程式),編譯器和處理器怎麼優化都行。JMM這麼做的原因是:程式員對于這兩個操作是否真的被重排序并不關心,程式員關心的是程式執行時的語義不能被改變(即執行結果不能被改變)。是以,happens-before關系本質上和as-if-serial語義是一回事。
- as-if-serial語義保證單線程内程式的執行結果不被改變,happens-before關系保證正确同步的多線程程式的執行結果不被改變。
- as-if-serial語義給編寫單線程程式的程式員創造了一個幻境:單線程程式是按程式的順序來執行的。happens-before關系給編寫正确同步的多線程程式的程式員創造了一個幻境:正确同步的多線程程式是按happens-before指定的順序來執行的。
as-if-serial語義和happens-before這麼做的目的,都是為了在不改變程式執行結果的前提下,盡可能地提高程式執行的并行度。
實時内容請關注微信公衆号,公衆号與部落格同時更新:程式員星星
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHL9kkaOBTWq1keJRVT3V1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZwpmLyEjMxMTOygDMwIjMwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)