文章已同步至GitHub開源項目: JVM底層原了解析
Java記憶體模型
JVM虛拟機規範中曾經試圖定義一種Java記憶體模型,來屏蔽掉各種硬體和作業系統的記憶體通路差異,以實作讓Java程式在各種平台下都可以達到一緻性的記憶體通路效果。
然而定義這樣一套記憶體模型并非很容易,這個模型必須足夠嚴謹,才能讓Java的并發記憶體通路操作不會有歧義。但是也必須足夠寬松,這樣使得虛拟機的具體實作能夠有自由的發揮空間來利用各種硬體的優勢。經過長時間的驗證和彌補,到了JDK1.5(實作了JSR133規範)之後,Java記憶體模型才終于成熟起來了。
主記憶體和工作記憶體
Java記憶體模型規定了所有的變量都存儲在
主記憶體
(Main Memory)中,每條線程都有自己的
工作記憶體
(Work Memory)
- 工作記憶體中儲存了被該線程使用的變量的主記憶體副本,
- 線程對變量的讀寫操作必須在工作記憶體中進行。
- 而不能直接通路主記憶體的資料。
- 不同的線程也不能互相讀寫對方的工作記憶體,線程之間的變量傳遞必須通過主記憶體傳遞。

主記憶體和工作記憶體的互動
Java記憶體模型定義了如下八種操作(每一種操作都是 原子的
, 不可再分
的)
原子的
不可再分
-
: 作用于主記憶體,将一個變量辨別為線程獨占狀态lock 鎖定
-
: 作用于主記憶體,将一個線程獨占狀态的變量釋放unlock: 解鎖
-
: 從主記憶體讀取資料到工作記憶體,便于之後的load操作read 讀取
-
: 把read讀取操作從主記憶體中得到的變量放入工作記憶體的變量副本中load 載入
-
: 将工作記憶體中的變量傳遞給執行引擎 當虛拟機遇到一個需要使用變量值的位元組碼時,執行此操作use 使用
-
: 将執行引擎中的值賦給工作記憶體的變量。 當虛拟機遇到一個指派操作時,執行此操作assign指派
-
: 将工作記憶體的值傳遞到主記憶體 ,便于之後的write操作store存儲
-
:将store存儲操作中從工作記憶體中擷取的變量寫入到主記憶體中write寫入
舉例:
- 如果要把一個變量從主記憶體拷貝到工作記憶體,則依次執行read讀取操作, load載入操作
- 如果要把一個變量從工作記憶體寫入到主記憶體,則依次執行store存儲操作,write寫入操作
上述的8種操作必須滿足以下規則:
- 不允許read和load、store和write操作之一單獨出現。也就是說不允許一個變量從主記憶體讀取但是工作記憶體不接受,也不允許工作記憶體發起回寫請求但是主記憶體不接受。
- 不允許一個線程丢棄它的最近assign的操作,即變量在工作記憶體中改變了之後必須同步到主記憶體中。
- 不允許一個線程無原因地(沒有發生過任何assign操作)把資料從工作記憶體同步回主記憶體中。
- 一個新的變量隻能在主記憶體中誕生,不允許在工作記憶體中直接使用一個未被初始化(load或assign)的變量。即就是對一個變量實施use和store操作之前,必須先執行過了assign和load操作。
- 一個變量在同一時刻隻允許一條線程對其進行lock操作,但lock操作可以被同一條線程重複執行多次,多次執行lock後,隻有執行相同次數的unlock操作,變量才會被解鎖。lock和unlock必須成對出現
- 如果對一個變量執行lock操作,将會清空工作記憶體中此變量的值,在執行引擎使用這個變量前需要重新執行load或assign操作初始化變量的值
- 如果一個變量事先沒有被lock操作鎖定,則不允許對它執行unlock操作;也不允許去unlock一個被其他線程鎖定的變量。
- 對一個變量執行unlock操作之前,必須先把此變量同步到主記憶體中(執行store和write操作)。
volatile特殊規則
volatile可以說是Java虛拟機提供的最輕量級的同步機制。但是它并不容易被正确,完整的了解。
Java記憶體模型中規定
當一個變量被定義為
volatile
之後,表示着線程工作記憶體無效,對此值的讀寫操作都會直接作用在主記憶體上,
是以它具備對所有線程的
立即可見性
。