10、volatile作用與原理
作用:
1)、防止重排序
例項化一個物件分為3步:
//1.分配物件記憶體空間
memory = allocate();
//2.初始化物件
instance(memory);
//3.設定instance指向剛分配的記憶體地址,此時instance!=null
instance = memory;
其中由於2、3不存在資料依賴關係,所以2、3步驟可能會重排序,
解決辦法就是將變數設定為volatile型別。
2)、實現可見性
當多執行緒對同一個變數的值進行修改時,獲取的結果並不是預期的值。
為了達到執行緒安全。將變數設定為volatile變數時,
JMM會把該執行緒對應的工作記憶體中的共享變數值重新整理到主記憶體中,
當讀取一個volatile變數時,JMM會把該執行緒對應的工作記憶體置為無效,
那麼該執行緒將只能從主記憶體中重新讀取共享變數,保證變數值最新。
原理:
在對volatile變數修飾的共享變數進行寫操作的時候,會多一行彙編程式碼:
0x01a3de1d: movb $0x0,0x1104800(%esi);0x01a3de24: lock addl $0x0,(%esp);
lock字首的指令在多核處理器下有2個操作:
1)、將當前處理器快取行的資料會寫回到系統記憶體;
2)、寫回記憶體的操作會引起在其他CPU裡快取了該記憶體地址的資料無效。
lock字首指令其實就相當於一個記憶體屏障(一個CPU指令),
volatile的底層就是通過記憶體屏障來實現的。
處理流程如下:
在對volatile變數進行寫操作,JVM就會向處理器傳送一條Lock字首的指令,
將這個變數所在快取行的資料寫回到系統記憶體。但是就算寫回到記憶體,
如果其他處理器快取的值還是舊的,再執行計算操作就會有問題,所以在多理器下,
為了保證各個處理器的快取是一致的,就會實現快取一致性協議(MESI),每個處理器
通過嗅探在匯流排上傳播的資料來檢查自己快取的值是不是過期了,當處理器發現自己
快取行對應的記憶體地址被修改,就會將當前處理器的快取行設定成無效狀態,
當處理器要對這個資料進行修改操作的時候,會強制重新從系統記憶體裡把資料
讀到處理器快取裡。複製程式碼