volatile的記憶體語義

2021-10-08 21:59:30 字數 2520 閱讀 8038

volatile的應用與底層原理詳見:《volatile的應用與底層原理》

class

volatilefeature***ample

public

void getandincrement (

)public

long

get(

)}

假設有多個執行緒分別呼叫上面程式的3個方法,這個程式在語義上和下面程式等價。

class

volatilefeature***ample2

public

void getandincrement (

)public

synchronized

long

get(

)}

如上面示例程式所示,乙個volatile變數的單個讀/寫操作與乙個普通變數的讀/寫操作都是使用同乙個鎖來同步,它們之間的執行效果相同

保證釋放鎖和獲取鎖的兩個執行緒之間的記憶體可見性

這意味著對乙個volatile變數的讀總是能看到(任意執行緒)對這個volatile變數最後的寫入

總結

class

volatileexample

public

void

reader()

}}

這裡a執行緒寫乙個volatile變數後b執行緒讀同乙個volatile變數a執行緒在寫volatile變數之

前所有可見的共享變數,在b執行緒讀同乙個volatile變數後,將立即變得對b執行緒可見

volatile寫的記憶體語義如下:下面來看看jmm如何實現volatile寫/讀的記憶體語義。為了實現volatile記憶體語義,jmm會分別限制這兩種型別的重排序型別。下表是jmm針對 編譯器制定的volatile重排序規則表

從表中我們可以總結出:

為了實現volatile的記憶體語義,編譯器在生成位元組碼時,會在指令序列中插入記憶體屏障來禁止特定型別的處理器重排序。對於編譯器來說,發現乙個最優布置來最小化插入屏障的總

幾乎不可能。為此,jmm採取保守策略。下面是基於保守策略的jmm記憶體屏障插入策略。

下面是保守策略下,volatile寫插入記憶體屏障後生成的指令序列示意圖

下面是在保守策略下,volatile讀插入記憶體屏障後生成的指令序列示意圖

上述volatile寫和volatile讀的記憶體屏障插入策略非常保守。

在實際執行時,只要不改變volatile寫-讀的記憶體語義,編譯器可以根據具體情況省略不必要的屏障。下面通過具體的示例**進行說明。

class

volatilebarrierexample

// 其他方法

}

針對readandwrite()方法,編譯器在生成位元組碼時可以做如下的優化。

最後的storeload屏障不能省略。因為第二個volatile寫之後,方法立即return。此時編

譯器可能無法準確斷定後面是否會有volatile讀或寫,為了安全起見,編譯器通常會在這裡插

入乙個storeload屏障。

記憶體屏障的插入還可以根據具體的處理器記憶體模型繼續優化。以x86處理器為例,下圖中除最後的storeload屏障外,其他的屏障都會被省略。

volatile記憶體語義

1 可見性 不管是volatile讀還是volatile寫,都會重新訪問主記憶體取最新的值 2 有序性 防止重排序 volatile寫前 storestore屏障 首先先把前面的普通寫操作執行到主記憶體,在執行volatile寫操作執行到記憶體 保證前面普通寫操作和volatile寫操作不重排,前面...

volatile的記憶體語義

一 保證記憶體的可見性 二 對任意單個volatile變數的讀 寫具有原子性,但類似於volatile 這種復合操作不具有原子性 三 禁止指令的重排序 解析 一 對於使用volatile關鍵字的共享變數,會強制將修改後的值立即寫入主記憶體,並會使得其他處理器裡面的快取行失效 嗅探機制,快取一致性協議...

volatile記憶體語義的實現

為了實現volatile的記憶體語義,編譯器在生成位元組碼時,會把指令序列中插入記憶體屏障來禁止特定型別的處理器重排序。下面是基於保守策略的jmm記憶體屏障插入策略 在每個volatile寫操作前面插入乙個storestore屏障 在每個volatile寫操作後面插進入乙個storeload屏障 在...