1. 計算機多核併發緩存架構
在多處理器系統中,每個處理器都有自己的高速緩存,而它們又共享同一主內存(MainMemory):
2. JMM內存模型
JMM數據原子操作:
read (讀取) :從主內存讀取數據
load (載入) :將主內存讀取到的數據寫入工作內存
use(使用) :從工作內存讀取數據來計算
assign (賦值) :將計算好的值重新賦值到工作內存中
store (存儲) :將工作內存數據寫入主內存
write (寫入) :將store過去的變量值賦值給主內存中的變量
lock (鎖定) :將主內存變量加鎖,標識爲線程獨佔狀態
unlock (解鎖) :將主內存變量解鎖解鎖後其他線程可以鎖定該變量
3. 案例
package com.tuling.jvm;
public class JMMThreadMemoryModel_Test {
public static volatile boolean initFlag = false ;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
System.out.println("waiting data");
System.out.println("initFlag:"+initFlag);
while (!initFlag){
}
System.out.println("+++++++++++++success");
}).start();
Thread.sleep(2000);
new Thread(()->{
System.out.println("prepare data ...");
initFlag = true;
System.out.println("end prepare data ...");
}).start();
}
}
如果initFlag 沒有volatile
關鍵字修飾,線程1等待initFlag變爲true纔會停止,而線程2兩秒之後啓動,對initFlag修改後線程1並不能感知到。
MESI緩存一致性協議
多個cpu從主內存讀取同一個數據到各自的高速緩存,當其中某個cpu修改了緩存裏的數據,該數據會馬上同步回主內存,其它cpu通過總線嗅探機制可以感知到數據的變化從而將自己緩存裏的數據失效。
Volatile緩存可見性實現原理
底層實現主要是通過彙編lock前綴指令,它會鎖定這塊內存區域的緩存(緩存行鎖定)並回寫到主內存
IA-32架構軟件開發者手冊對lock指令的解釋:
1)會將當前處理器緩存行的數據立即寫回到系統內存。(如果沒有volatile修飾,該變量的修改何時寫入主內存是不確定的)
2)這個寫回內存的操作在到達總線時會引起在其他CPU裏緩存了該內存地址的數據無效(MESI協議)
那麼存在一個問題:如果write操作還沒完成,其它線程又進行read,此時讀到的值仍然是失效前的。
所以在store前,會有一個加鎖的操作,在write完成後釋放鎖。
併發編程三大特性:可見性,原子性,有序性
volatile保證可見性與有序性,但是不保證原子性,保證原子性需要藉助
synchronized這樣的鎖機制。
原圖:https://www.processon.com/diagraming/5e84458ae4b08c5b86f779ff