天天看點

Java記憶體模型:解決可見性和有序性問題Java記憶體模型:解決可見性和有序性問題

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)圖例講解:

  1. “x=42” Happens-Before 寫變量 “v=true” ,這是【程式的順序性規則】的内容;
  2. 寫變量“v=true” Happens-Before 讀變量 “v=true”,這是【Volatile 變量規則】的内容 。
  3. 再根據【傳遞性規則】可得:“x=42” Happens-Before 讀變量“v=true”,即線程B能看到“x=42”
Java記憶體模型:解決可見性和有序性問題Java記憶體模型:解決可見性和有序性問題

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() 操作的傳回

Java記憶體模型:解決可見性和有序性問題Java記憶體模型:解決可見性和有序性問題

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記憶體模型思維導圖

Java記憶體模型:解決可見性和有序性問題Java記憶體模型:解決可見性和有序性問題

參考文獻:

[1]王寶令. Java并發程式設計實戰[M]. 極客時間, 2019.

繼續閱讀