Java并發(fā) - Happens-before規(guī)則

0. 前言

介紹Happens-before規(guī)則前,需要對JMM(Java Memory Model)有一定了解(可以閱讀參考目錄中6-12(強烈推薦))。
簡單來說,程序員寫程序是按照順序一致性模型編寫代碼,目的是得到正確的執(zhí)行結(jié)果,即順序一致性模型下的執(zhí)行結(jié)果。但是出于執(zhí)行效率等原因,編譯器在編譯時、處理器在執(zhí)行時可能進行指令重排以提高程序運行效率,JMM保證在正確同步的情況下得到的結(jié)果與順序一致性模型得到的結(jié)果相同。

0.1 為什么會執(zhí)行結(jié)果可能會有不同?

順序一致性模型中,只有一個單一的全局內(nèi)存,所有線程的操作直接在此內(nèi)存中反映,對后續(xù)執(zhí)行的線程可見。

順序一致性內(nèi)存模型示意圖

但是在java中,系統(tǒng)的資源分配是以進程為單位,線程享有所在進程的資源,但是每個線程會有自己的工作內(nèi)存(包括但不限于寄存器、高速緩存等),工作內(nèi)存中的內(nèi)容是主內(nèi)存中內(nèi)容的拷貝,線程執(zhí)行時修改的是工作內(nèi)存中的內(nèi)容,這樣的話修改就不一定會被別的線程觀察到。


java多線程運行內(nèi)存示意圖

1. 并發(fā)的三大基礎(chǔ)概念

原子性、可見性、有序性

1.1 原子性

一個操作集合,要么全部執(zhí)行,要么全不執(zhí)行,即不能在執(zhí)行的中途中斷。

一般來說只有簡單的讀取、賦值(不包括變量間的賦值)、CAS操作才是原子操作。
tip:java中的long、double類型的變量的讀寫是不具有原子性的。
java中保證原子操作的手段:synchronized、lock、volatile(對long、double)、CAS等。

1.2 可見性

當(dāng)多個線程訪問共享變量時,某個線程對其作出修改后,其他線程是否能觀察到此修改。

因為線程執(zhí)行時,修改的是工作內(nèi)存的數(shù)據(jù),工作內(nèi)存對其他線程是不可見的,所以在此修改同步到主內(nèi)存之前,對其他線程來說,此修改都是不可見的。
java中保證可見性的手段:鎖、volatile。
其中可見性關(guān)系的描述即為Happens-before規(guī)則,如果A Happens-before B則A中的修改均對B可見。

1.3 有序性

程序的執(zhí)行順序按照代碼的先后順序執(zhí)行。

由于JMM允許編譯器和處理器進行指令重排來進行優(yōu)化,這種優(yōu)化如果沒有限制條件的話可能會影響多線程程序的執(zhí)行結(jié)果,所以要添加一定的限制條件,即Happens-before規(guī)則。
如果兩個操作具有Happens-before關(guān)系,則會保證他們的有序性。

2. Happens-before規(guī)則

Happens-before規(guī)則具體包含以下內(nèi)容:

  1. 程序次序規(guī)則:一個線程內(nèi),按照代碼順序,書寫在前面的操作先行發(fā)生于書寫在后面的操作;
  2. 鎖定規(guī)則:一個unLock操作先行發(fā)生于后面對同一個鎖額lock操作;
  3. volatile變量規(guī)則:對一個變量的寫操作先行發(fā)生于后面對這個變量的讀操作;
  4. 傳遞規(guī)則:如果操作A先行發(fā)生于操作B,而操作B又先行發(fā)生于操作C,則可以得出操作A先行發(fā)生于操作C;
  5. 線程啟動規(guī)則:Thread對象的start()方法先行發(fā)生于此線程的每個一個動作;
  6. 線程中斷規(guī)則:對線程interrupt()方法的調(diào)用先行發(fā)生于被中斷線程的代碼檢測到中斷事件的發(fā)生;
  7. 線程終結(jié)規(guī)則:線程中所有的操作都先行發(fā)生于線程的終止檢測,我們可以通過Thread.join()方法結(jié)束、Thread.isAlive()的返回值手段檢測到線程已經(jīng)終止執(zhí)行;
  8. 對象終結(jié)規(guī)則:一個對象的初始化完成先行發(fā)生于他的finalize()方法的開始;

3. 總結(jié)

寫代碼時是按照順序一致性模型寫代碼,但是在實際情況下,由于工作內(nèi)存的存在和優(yōu)化的需求,多線程時程序的結(jié)果并不一定是想要的結(jié)果,為了避免這種情況發(fā)生,使用volatile/Lock等手段,保證滿足Happens-before規(guī)則的代碼的正確性,從而保證程序結(jié)果的正確性。

4. 參考

  1. happens-before俗解
  2. 【死磕Java并發(fā)】-----Java內(nèi)存模型之happens-before
  3. 【Java并發(fā)編程】之十六:深入Java內(nèi)存模型——happen-before規(guī)則及其對DCL的分析(含代碼)
  4. 【死磕Java并發(fā)】—–深入分析volatile的實現(xiàn)原理
  5. 你真的了解volatile關(guān)鍵字嗎?
  6. 深入理解Java內(nèi)存模型(一)——基礎(chǔ)
  7. 深入理解Java內(nèi)存模型(二)——重排序
  8. 深入理解Java內(nèi)存模型(三)——順序一致性
  9. 深入理解Java內(nèi)存模型(四)——volatile
  10. 深入理解Java內(nèi)存模型(五)——鎖
  11. 深入理解Java內(nèi)存模型(六)——final
  12. 深入理解Java內(nèi)存模型(七)——總結(jié)
  13. 線程的工作內(nèi)存
  14. 同一進程中的線程究竟共享哪些資源
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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