JMM
JMM:Java memory model
Java記憶體模型,不存在的東西,就是一個概念
作用:快取一致性協議,用於定義資料讀寫的規則。
Java中主記憶體只有一個,每一條執行緒有自己的工作記憶體
JMM定義了執行緒工作記憶體和主記憶體之間的抽象關係:執行緒之間的共享變數儲存在主記憶體(Main Memory)中,每個執行緒都有一個私有的本地記憶體(Local Memory)。
在多執行緒環境下,執行緒之間的通訊,就不得不提JMM(java記憶體模型)
在JVM內部使用的java記憶體模型(JMM)將執行緒堆疊和堆之間的記憶體分開
執行緒堆疊(thread stack
):
1.執行在java虛擬機器上的每個執行緒都有自己的執行緒堆疊(
thread stack
)
2.執行緒堆疊還包含正在執行的每個方法的所有區域性變數,一個執行緒只能訪問它自己的執行緒堆疊。由執行緒建立的區域性變數對於除建立它的執行緒之外的所有其他執行緒都是不可見的。
3.即使兩個執行緒正在執行完全相同的程式碼,兩個執行緒仍然會在每個執行緒堆疊中建立該程式碼的區域性變數,一個執行緒可能會將一個有限變數的副本傳遞給另一個執行緒,但它不能共享原始區域性變數本身
堆:
1.堆包含在Java應用程式中建立的所有物件,而不管是不是由執行緒建立的該物件。
2.堆中的物件可以被具有物件引用的所有執行緒訪問。當一個執行緒訪問一個物件時,它也可以訪問該物件的成員變數。
3.如果兩個執行緒同時呼叫同一個物件上的一個方法,它們都可以訪問該物件的成員變數,但每個執行緒都有自己的區域性變數副本
4.堆中的資料是共享的,執行緒不安全的
Java記憶體模型帶來的問題
可見性問題
CPU中執行的執行緒從主存中複製共享物件obj到它的CPU快取,把物件obj的count變數改為2。但這個變更對執行在右邊CPU中的執行緒不可見,因為這個更改還沒有flush到主存中:要解決共享物件可見性這個問題,我們可以使用java volatile關鍵字或者是加鎖
記憶體互動操作
記憶體互動操作有8種,虛擬機器實現必須保證每一個操作都是原子的,不可在分的(對於double和long型別的變數來說,load、store、read和write操作在某些平臺上允許例外)
- lock (鎖定):作用於主記憶體的變數,把一個變數標識為執行緒獨佔狀態
- unlock (解鎖):作用於主記憶體的變數,它把一個處於鎖定狀態的變數釋放出來,釋放後的變數才可以被其他執行緒鎖定
- read (讀取):作用於主記憶體變數,它把一個變數的值從主記憶體傳輸到執行緒的工作記憶體中,以便隨後的load動作使用
- load (載入):作用於工作記憶體的變數,它把read操作從主存中變數放入工作記憶體中
- use (使用):作用於工作記憶體中的變數,它把工作記憶體中的變數傳輸給執行引擎,每當虛擬機器遇到一個需要使用到變數的值,就會使用到這個指令
- assign (賦值):作用於工作記憶體中的變數,它把一個從執行引擎中接受到的值放入工作記憶體的變數副本中
- store (儲存):作用於主記憶體中的變數,它把一個從工作記憶體中一個變數的值傳送到主記憶體中,以便後續的write使用
- write (寫入):作用於主記憶體中的變數,它把store操作從工作記憶體中得到的變數的值放入主記憶體的變數中
JMM對這八種指令的使用,制定瞭如下規則:
- 不允許read和load、store和write操作之一單獨出現。即使用了read必須load,使用了store必須write
- 不允許執行緒丟棄他最近的assign操作,即工作變數的資料改變了之後,必須告知主存
- 不允許一個執行緒將沒有assign的資料從工作記憶體同步回主記憶體
- 一個新的變數必須在主記憶體中誕生,不允許工作記憶體直接使用一個未被初始化的變數。就是懟變數實施use、store操作之前,必須經過assign和load操作
- 一個變數同一時間只有一個執行緒能對其進行lock。多次lock後,必須執行相同次數的unlock才能解鎖
- 如果對一個變數進行lock操作,會清空所有工作記憶體中此變數的值,在執行引擎使用這個變數前,必須重新load或assign操作初始化變數的值。
- 如果一個變數沒有被lock,就不能對其進行unlock操作。也不能unlock一個被其他執行緒鎖住的變數
- 對一個變數進行unlock操作之前,必須把此變數同步回主記憶體
JMM對這八種操作規則和對volatile的一些特殊規則就能確定哪裡操作是執行緒安全,哪些操作是執行緒不安全的了。但是這些規則實在複雜,很難在實踐中直接分析。所以一般我們也不會透過上述規則進行分析。更多的時候,使用java的happen-before規則來進行分析。
Volatile是Java虛擬機器提供的輕量級的同步機制
- 保證可見性
- 不保證原子性
- 指令重排
關於JMM的一些同步約定:
- 執行緒解鎖前,必須把共享變數立刻刷回主存
- 執行緒加鎖前,必須讀取主存中的最新值到工作記憶體中
- 加鎖和解鎖是同一把鎖
執行緒:工作記憶體、主記憶體
本作品採用《CC 協議》,轉載必須註明作者和本文連結