Java記憶體模型:解決可見性和有序性問題
01 | Java 記憶體模型
1、導讀
1)可見性、原子性、有序性是并發程式設計的 Bug 之源
2) Java 記憶體模型是面試熱點,同時也是分析并發問題的基礎
3)Java 記憶體模型是個很複雜的規範,可以從不同的視角來解讀
2、Java 記憶體模型作用
1)解決可見性
2)解決有序性
3、簡析出現Java 記憶體模型的原因
1)緩存和編譯優化分别導緻可見性和有序性問題,一刀切禁用雖然可以解決問題,但嚴重影響性能,合理的方案是按需禁用緩存以及編譯優化。
2)為了解決可見性和有序性問題,隻需要提供給程式員按需禁用緩存和編譯優化的方法即可。
4、關鍵點
1)本質:從程式員視角可以了解為,Java 記憶體模型規範了 JVM 如何提供按需禁用緩存和編譯優化的方法。
2)按需禁用的方法:包括 volatile、synchronized 和 final 三個關鍵字,以及六項 Happens-Before 規則
02 | 三個關鍵字:volatile、synchronized、final
1、volatile
1)定義:告訴編譯器,對這個變量的讀寫,不能使用 CPU 緩存,必須從記憶體中讀取或者寫入
2)Java 記憶體模型在 1.5 版本對 volatile 語義進行了增強,Java 記憶體模型利用Happens-Before 規則,使 volatile 圓滿解決了CPU 緩存導緻可見性問題
2、synchronized
3、final
1)volatile 表示的是禁用緩存以及編譯優化
2)final 修飾變量時,初衷是告訴編譯器:這個變量生而不變,可以可勁兒優化
3)使用final要注意“逸出”問題。
03 | 六項 Happens-Before 規則
1、導讀
1)Happens-Before 它真正要表達的是:前面一個操作的結果對後續操作是可見的
2)比較正式的說法:Happens-Before 限制了編譯器的優化行為,雖允許編譯器優化,但是要求編譯器優化後一定遵守 Happens-Before 規則
3)Happens-Before 規則中和程式員相關的規則一共有六項,都是關于可見性的。
2、程式的順序性規則
1)定義:指在一個線程中,按照程式順序,前面的操作 Happens-Before 于後續的任意操作
2)通俗了解:程式前面對某個變量的修改一定是對後續操作可見的。
3、Volatile 變量規則
1)定義:指對一個 volatile 變量的寫操作, Happens-Before 于後續對這個 volatile 變量的讀操作
2)關聯傳遞性規則更容易了解。
4、傳遞性
1)定義:指如果 A Happens-Before B,且 B Happens-Before C,那麼 A Happens-Before C
2)圖例講解:
- “x=42” Happens-Before 寫變量 “v=true” ,這是【程式的順序性規則】的内容;
- 寫變量“v=true” Happens-Before 讀變量 “v=true”,這是【Volatile 變量規則】的内容 。
- 再根據【傳遞性規則】可得:“x=42” Happens-Before 讀變量“v=true”,即線程B能看到“x=42”
5、管程中鎖的規則
1)定義:指對一個鎖的解鎖 Happens-Before 于後續對這個鎖的加鎖
2)管程是一種通用的同步原語,在 Java 中指的就是 synchronized,synchronized 是 Java 裡對管程的實作
3)管程中的鎖在 Java 裡是隐式實作的,加鎖以及釋放鎖都是編譯器幫我們實作的。
4)例子講解:線程A通過加鎖-修改變量(x由10變為25)-解鎖,線程B獲得鎖後,可以看到線程A修改後的變量值(x=25)。
6、線程 start() 規則
1)定義:它是指主線程 A 啟動子線程 B 後,子線程 B 能夠看到主線程在啟動子線程 B 前的操作
2)通俗來講,如果線程 A 調用線程 B 的 start() 方法(即線上程 A 中啟動線程 B),那麼該 start() 操作 Happens-Before 于線程 B 中的任意操作
7、線程 join() 規則
1)定義:指主線程 A 等待子線程 B 完成(主線程 A 通過調用子線程 B 的 join() 方法實作),當子線程 B 完成後(主線程 A 中 join() 方法傳回),主線程能夠看到子線程的操作
2)通俗來講,如果線上程 A 中,調用線程 B 的 join() 并成功傳回,那麼線程 B 中的任意操作 Happens-Before 于該 join() 操作的傳回
04 | 小結
1、導讀
1)Java 記憶體模型裡面,最晦澀的部分就是 Happens-Before 規則了
2)在現實世界裡,如果 A 事件是導緻 B 事件的起因,那麼 A 事件一定是先于(Happens-Before)B 事件發生的,這個就是 Happens-Before 語義的現實了解
2、Happens-Before 的語義本質
1)在 Java 語言裡面,Happens-Before 的語義本質上是一種可見性
2)定義:A Happens-Before B 意味着 A 事件對 B 事件來說是可見的,無論 A 事件和 B 事件是否發生在同一個線程裡
3)例子:A 事件發生線上程 1 上,B 事件發生線上程 2 上,Happens-Before 規則保證線程 2 上也能看到 A 事件的發生
3、Java 記憶體模型由兩部分組成
1)一部分面向編寫并發程式的應用開發人員,該部分核心内容是 Happens-Before 規則
2)另一部分面向 JVM 的實作人員
05 | 思維導圖
1、Java記憶體模型思維導圖
參考文獻:
[1]王寶令. Java并發程式設計實戰[M]. 極客時間, 2019.