對volatile關鍵字的理解

2022-07-30 10:00:11 字數 1633 閱讀 6413

如果volatile變數與普通變數發⽣了重排序,雖然volatile變數能保證記憶體可⻅性,但是可能導致普通變數讀取錯誤

jvm通過記憶體屏障來實現限制處理器的重排序。編譯器在生成位元組碼時,會在指令序列中插入記憶體屏障來禁止特定型別的處理器重排序

編譯器選擇了⼀個⽐較保守的jmm記憶體屏障插⼊策略,這樣可以保證在任何處理器平台,任何程式中都能得到正確的volatile記憶體語義。這個策略是:

如果第乙個操作是volatile讀,那麼不管第二個操作是什麼,都不能重排序

如果第二個操作是volatile寫,那麼不管第乙個操作是什麼,都不能重排序

如果第乙個操作是volatile寫,第二個操作是volatile讀,那麼不能重排序

如果第乙個操作是普通變數讀,第二個操作是volatile讀,那麼可以重排序,如下:

// 宣告變數

int a = 0; // 宣告普通變數

volatile boolean flag = false; // 宣告volatile變數

// 以下兩個變數的讀操作是可以重排序的

int i = a; // 普通變數讀

boolean j = flag; // volatile變數讀

從volatile的記憶體語義上來看,volatile可以保證記憶體可⻅性且禁⽌重排序。在保證記憶體可⻅性這⼀點上,volatile有著與鎖相同的記憶體語義,所以可以作為⼀個「輕量級」的鎖來使⽤。

但由於volatile僅僅保證對單個volatile變數的讀/寫具有原⼦性,⽽鎖可以保證整個臨界區**的執⾏具有原⼦性。

所以在功能上,鎖⽐volatile更強⼤;在效能上,volatile更有優勢

volatile可以用於單例模式的雙檢鎖檢查寫法

如果這⾥的變數宣告不使⽤volatile關鍵字,是可能會發⽣錯誤的。它可能會被重排序:

instance = new singleton(); // 第10⾏

// 可以分解為以下三個步驟

1 memory=allocate();// 分配記憶體 相當於c的malloc

2 ctorinstanc(memory) //初始化物件

3 s=memory //設定s指向剛分配的位址

// 上述三個步驟可能會被重排序為 1-3-2,也就是:

1 memory=allocate();// 分配記憶體 相當於c的malloc

3 s=memory //設定s指向剛分配的位址

2 ctorinstanc(memory) //初始化物件

⽽⼀旦假設發⽣了這樣的重排序,⽐如執行緒a在第10⾏執⾏了步驟1和步驟3,但是步驟2還沒有執⾏完。這個時候執行緒a執⾏到了第7⾏,它會判定instance不為空,然後直接返回了⼀個未初始化完成的instance!

本文參考於這裡

對volatile關鍵字的理解

來看下面這樣乙個例子 public class threadtest backgroundthread.start timeunit.seconds.sleep 1 stoprequested true 我們希望backgroundthread 執行緒能夠在睡眠一秒後停止,但是實際情況是執行緒陷入了...

對volatile關鍵字的理解

volatile關鍵字是給編譯器看的,c語言是一種操作性語言,與硬體底層關係比較密切,尤其是在嵌入式領域。比如如下程式 a 1 a 2 a 4 printf d n a 經過編譯器優化後可能就變成了 a 4 printf d n a 無形中a 1和 a 2的操作就被省去了,表面是沒問題,但是在嵌入式...

Volatile關鍵字理解

物理角度 由於計算機的儲存裝置和cpu的運算速度有幾個數量級的差距,所以現代計算機系統加入一層速度接近 cpu的快取記憶體 cache 但cache帶來乙個問題 快取一致性問題 在多處理器系統中,每個處理器機油自己的cache 工作記憶體 又共享同一主記憶體。舉例 當程式在執行過程中,會將運算需要的...