為什么volatile能保證可見性? (內存屏障)


 

我們都知道volatile能保證可見性,不能保證原子性,比如i++操作

也知道Happen-Before原則,那么是如何確保Happen-Before原則不被指令重排序影響呢?

例如你讓一個volatile的integer自增(i++),其實要分成3步:

1)讀取volatile變量值到local; 

2)增加變量的值;

3)把local的值寫回,讓其它的線程可見。

這3步的jvm指令為:

 
mov   
0xc(%r10),%r8d
 ; Load
inc   
 %r8d           ; Increment
mov   
 %r8d,0xc(%r10)
 ; Store
lock
 addl $0x0,(%rsp)
 ; StoreLoad Barrier

 

StoreLoad Barrier就是內存屏障

內存屏障(memory barrier) 是一個CPU指令。基本上,它是這樣一條指令: a) 確保一些特定操作執行的順序; b) 影響一些數據的可見性(可能是某些指令執行后的結果)。編譯器和CPU可以在保證輸出結果一樣的情況下對指令重排序,使性能得到優化。插入一個內存屏障, 相當於告訴CPU和編譯器先於這個命令的必須先執行,后於這個命令的必須后執行。內存屏障另一個作用是強制更新一次不同CPU的緩存。例如,一個寫屏障會 把這個屏障前寫入的數據刷新到緩存,這樣任何試圖讀取該數據的線程將得到最新值,而不用考慮到底是被哪個cpu核心或者哪顆CPU執行的。

內存屏障和volatile什么關系?上面的虛擬機指令里面有提到,如果你的字段是volatile,Java內存模型將在寫操作后插入一個寫屏障 指令,在讀操作前插入一個讀屏障指令。這意味着如果你對一個volatile字段進行寫操作,你必須知道:1、一旦你完成寫入,任何訪問這個字段的線程將 會得到最新的值。2、在你寫入前,會保證所有之前發生的事已經發生,並且任何更新過的數據值也是可見的,因為內存屏障會把之前的寫入值都刷新到緩存。 

明白了內存屏障這個CPU指令,回到前面的JVM指令:從Load到store到內存屏障,一共4步,其中最后一步jvm讓這個最新的變量的值在所有線程可見,也就是最后一步讓所有的CPU內核都獲得了最新的值,但中間的幾步(從Load到Store)是不安全的,中間如果其他的CPU修改了值將會丟失。

所以volatile不能保證i++操作的原子性

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM