Happens-Before原則到底規定了什麼

Happens-Before 規則

如何理解 Happens-Before 呢?如果望文生義(很多網文也都愛按字面意思翻譯成「先行發生」),那就南轅北轍了,Happens-Before 並不是說前面一個操作發生在後續操作的前面,它真正要表達的是:前面一個操作的結果對後續操作是可見的

就像有心靈感應的兩個人,雖然遠隔千里,一個人心之所想,另一個人都看得到。Happens-Before 規則就是要保證執行緒之間的這種「心靈感應」。所以比較正式的說法是:Happens-Before 約束了編譯器的優化行為,雖允許編譯器優化,但是要求編譯器優化後一定遵守 Happens-Before 規則。

在網上的許多資料中對於Happens-Before的7個規則定義如下:

  1. 程式次序規則:在一個執行緒內,按照程式程式碼順序,書寫在前面的操作先行發生於書寫在後面的操作。準確地說,應該是控制流順序而不是程式程式碼順序,因為要考慮分支、循環等結構。
  2. 管程鎖定規則:一個unlock操作先行發生於後面對同一個鎖的lock操作。這裡必須強調的是同一個鎖,而”後面”是指時間上的先後順序。
  3. volatile變數規則:對一個volatile變數的寫操作先行發生於後面對這個變數的讀操作,這裡的”後面”同樣是指時間上的先後順序。
  4. 執行緒啟動規則:Thread對象的start()方法先行發生於此執行緒的每一個動作。
  5. 執行緒終止規則:執行緒中的所有操作都先行發生於對此執行緒的終止檢測,我們可以通過Thread.join()方法結束、Thread.isAlive()的返回值等手段檢測到執行緒已經終止執行。
  6. 執行緒中斷規則:對執行緒interrupt()方法的調用先行發生於被中斷執行緒的程式碼檢測到中斷事件的發生,可以通過Thread.interrupted()方法檢測到是否有中斷髮生。
  7. 對象終結規則:一個對象的初始化完成(構造函數執行結束)先行發生於它的finalize()方法的開始。

上述描述的確實有點抽象,不如我們用自己的話來理解一番

1. 程式的順序性規則

這條規則是指在一個執行緒中,按照程式順序,前面的操作 Happens-Before 於後續的任意操作。

2. volatile 變數規則

這條規則是指對一個 volatile 變數的寫操作, Happens-Before 於後續對這個 volatile 變數的讀操作。

3. 傳遞性

這條規則是指如果 A Happens-Before B,且 B Happens-Before C,那麼 A Happens-Before C。

1.5 版本的並發工具包(java.util.concurrent)就是靠 volatile 語義來搞定可見性的

4. 管程中鎖的規則

這條規則是指對一個鎖的解鎖 Happens-Before 於後續對這個鎖的加鎖。

要理解這個規則,就首先要了解「管程指的是什麼」。管程是一種通用的同步原語,在 Java 中指的就是 synchronized,synchronized 是 Java 里對管程的實現。

管程中的鎖在 Java 里是隱式實現的(synchronized)

5. 執行緒 start() 規則

這條是關於執行緒啟動的。它是指主執行緒 A 啟動子執行緒 B 後,子執行緒 B 能夠看到主執行緒在啟動子執行緒 B 前的操作。

6. 執行緒 join() 規則

這條是關於執行緒等待的。它是指主執行緒 A 等待子執行緒 B 完成(主執行緒 A 通過調用子執行緒 B 的 join() 方法實現),當子執行緒 B 完成後(主執行緒 A 中 join() 方法返回),主執行緒能夠看到子執行緒的操作。當然所謂的「看到」,指的是對共享變數的操作。

Reference

Java並發編程實戰

Tags: