happen-before 可真是一個經典又老生常談的話題,規則一共就八條,但看起來總有種抓不住重點的感覺。今天再整理一下對這八條規則的理解。
首先我的理解是 happen-before 的語義與在什么什么之前發生完全沒有關系,其語義是如果 A hapen-bfore B,那么 A 的結果對 B 是可見的。通過這些規則可以保證程序按我們預想的方式運轉。
我個人理解中將該原則分兩部分理解,單線程與多線程環境下的HB。單線程下通過語義分析數據依賴關系,編譯器和處理器可以合理的優化我們的代碼。但是多線程情況下不同線程間的數據依賴關系有我們定義,處理器與編譯器都無法通過分析感知,HB 原則定義了某些特定場景下多線程間的數據依賴關系。
總的來說,HB 原則是對單線程環境下的指令重排序以及多線程環境下的線程間數據的一致性進行的約束。單線程情況下保證串行語義,多線程情況下因為數據的一致性需要我們自己聲明和保證,所以 JVM 自行保證了 HB 原則中提出的它認為必須要保證一致性的情況。
1. 單線程happen-before原則:在同一個線程中,書寫在前面的操作happen-before后面的操作。
首先是單線程的 HB ,前面的操作產生的結果必須對后面的操作可見。而不是前面的操作必須先於后面的操作執行,比如按照 as-if-serial 語義,沒有數據依賴的兩條指令是可以進行重排序的。而這種情況對於 HB 原則來說,因為兩條指令都沒有產生對方需要的結果,而不需要對對方可見,及時執行順序被調轉也是符合 HB 原則的。
2. 鎖的happen-before原則:同一個鎖的unlock操作happen-before此鎖的lock操作。
個人理解強調的是解鎖操作在多線程環境的可見性。一個線程進行了解鎖操作,對於晚於該操作的加鎖操作必須能夠及時感應到鎖的狀態變化。解鎖操作的結果對后面的加鎖操作一定是可見的,無論兩個是否在一個線程。
3. volatile的happen-before原則: 對一個volatile變量的寫操作happen-before對此變量的任意操作。
對 volatile 變量的寫操作的結果對於發生於其后的任何操作的結果都是可見的。x86 架構下volatile 通過內存屏障和緩存一致性協議實現了變量在多核心之間的一致性。
4. happen-before的傳遞性原則: 如果A操作 happen-before B操作,B操作happen-before C操作,那么A操作happen-before C操作。
HB 可以說是兩項操作之間的偏序關系,滿足偏序關系的各項性質,我們都知道偏序關系中有一條很重要的性質:傳遞性,所以Happens-Before也滿足傳遞性。這個性質非常重要,通過這個性質可以推導出兩個沒有直接聯系的操作之間存在Happens-Before關系
5. 線程啟動的happen-before原則:同一個線程的start方法happen-before此線程的其它方法。
start 放法與其它方法可能並沒有數據依賴關系,但是顯而易見的,為了程序的正確性,我們必須做到這一點。start 方法造成的函數副作用必須對其它方法可見。
6. 線程中斷的happen-before原則:對線程interrupt方法的調用happen-before被中斷線程的檢測到中斷發送的代碼。
interrupt 方法改變的狀態必須對后續執行的檢測方法可見。
7. 線程終結的happen-before原則:線程中的所有操作都happen-before線程的終止檢測。
為了安全的關閉線程,線程中的方法造成的函數副作用必須對線程關閉方法可見。
8. 對象創建的happen-before原則:一個對象的初始化完成先於他的finalize方法調用。
單線程下對象的創建於銷毀存在數據依賴,該條原則強調的是多線程情況下對象初始化的結果必須對發生於其后的對象銷毀方法可見。