volatile的理解
volatile關鍵字的作用
volatile用於修飾變數
- 保證變數的記憶體可見性
- 不保證原子性
- 禁止指令重排序
JMM模型
Java記憶體模型(Java Memory Model)是一種抽象的,本身不存在的,用來一組規範,來定製程式中各個變數的訪問方式.
JMM關於同步的規定
- 執行緒解鎖前,必須把共享變數的值寫回主記憶體.
- 執行緒加鎖前,必須讀取主記憶體中變數的最新值到自己工作記憶體.
- 加鎖與解鎖必須使用同一把鎖.
由於JVM執行程式的載體是執行緒,而JVM建立每個執行緒時都會為該執行緒分配一塊私有的工作記憶體(也稱為棧記憶體).JMM規定所有變數都儲存在主記憶體中,主記憶體是共享區域,所有執行緒都可以訪問.但執行緒對變數的操作(讀取賦值等)都必須在工作記憶體中進行,首先從主記憶體中讀取變數到工作記憶體中,然後對該變數進行操作,操作完成把該變數寫回主記憶體.各個執行緒的工作記憶體中儲存著主記憶體變數的拷貝副本.不同執行緒無法訪問對方的工作記憶體,因此必須通過主記憶體來完成.
可見性
不同執行緒各自的記憶體空間裡的變數是不能互相訪問的,稱為不可見性.
如果A執行緒修改了從主記憶體拷貝的一個變數,還未寫回主記憶體,而B執行緒從主記憶體中讀取了該變數,那麼此變數的值就是A執行緒修改前的值,這就造成了AB兩個執行緒的不可見問題.
原子性
原子性表示某個操作是不可拆分的.
比如i++操作,在底層被拆分成了三個操作:執行getfiled拿到原始的i,執行iadd進行+1操作,執行putfield操作把累加後的值寫回.
如果有變數i初始值為0,執行緒A在工作記憶體中執行i++操作,首先拿到原始i,但還未執行+1操作時,執行緒B得到了cpu時間,執行緒A阻塞了.執行緒B在工作記憶體中也執行了i++操作,然後把i的值1寫回了主記憶體.但是此時執行緒A不會再返回去讀取主記憶體的i值,此時A執行緒工作記憶體中的i值還是0,A執行緒繼續執行i++操作,然後把i的值1寫回到主記憶體.此時主記憶體的i值就是1,B執行緒的操作就被A執行緒覆蓋了.
有序性
為了儘可能的減少記憶體速度遠慢於cpu速度所帶來的cou閒置問題,虛擬機器會按照自己的特定規則,將程式的編寫順序打亂執行,以儘可能的充分利用cpu,提高執行效率.這個前提是亂序執行不會導致程式結果出錯.在單執行緒環境下,可以保證程式結果正確,但是在多執行緒環境下,重排序後的程式結果正確性是無法保證的.
volatile的實現原理
記憶體屏障(Memory Barrier),是一個cpu指令,作用有兩個:
- 保證特定操作的執行順序.
- 保證某些變數的記憶體可見性.
編譯器和處理器都可以執行指令重排序優化,如果在指令間插入一條記憶體屏障則會告訴編譯器和cpu,不管什麼指令都不能和這條記憶體屏障進行指令重排序,也就是通過插入記憶體屏障禁止在記憶體屏障的前後進行指令重排序.記憶體屏障另一個作用就是強制重新整理出cpu的各種快取資料,因此cpu上的所有執行緒都能讀取到這些資料的最新值.
volatile的使用場景
- 單例模式DCL(雙重檢查鎖)
class SingletonDemo2 {
public volatile static SingletonDemo2 instance = null;
private SingletonDemo2() {
}
public static SingletonDemo2 getInstance() {
if (null == instance) {
synchronized (SingletonDemo2.class) {
if (null == instance) {
instance = new SingletonDemo2();
}
}
}
return instance;
}
}
單例物件必須加volatile關鍵字的原因:
DCL不一定執行緒安全,因為有指令重排序的存在,加入volatile可以禁止指令重排序.初始化一個單例物件底層可分為以下步驟:
- 分配記憶體空間
- 初始化物件
- 設定物件指向剛分配的記憶體地址
步驟二和步驟三不存在資料依賴關係,而且指令重排後在單執行緒情況下不會改變程式的結果,因此這種重排優化是允許的.重排後會導致A執行緒拿到的instance不為null,但是此時instance未必已經完成初始化,也就造成了執行緒安全問題.
相關文章
- 理解volatile
- 死磕Java——volatile的理解Java
- 深入理解volatile
- 徹底理解volatile
- synchronized和volatile理解synchronized
- Java關鍵字volatile的理解Java
- 快速理解 volatile 關鍵字
- 怎樣正確理解volatile?
- 兩張圖理解volatile關鍵字
- 深入理解Java中的volatile關鍵字Java
- java多執行緒之volatile理解Java執行緒
- 理解並正確使用synchronized和volatilesynchronized
- 對volatile的理解--從JMM以及單例模式剖析單例模式
- 你說一下對Java中的volatile的理解吧Java
- 深入彙編指令理解Java關鍵字volatileJava
- volatile的用法
- Java併發專題(三)深入理解volatile關鍵字Java
- [C#.NET 拾遺補漏]10:理解 volatile 關鍵字C#
- 全面理解Java記憶體模型(JMM)及volatile關鍵字Java記憶體模型
- volatile
- 深入理解Java記憶體模型JMM與volatile關鍵字Java記憶體模型
- Volatile的底層原理
- Volatile關鍵字&&DCL單例模式,volatile 和 synchronized 的區別單例模式synchronized
- 對精緻碼農大佬的 [理解 volatile 關鍵字] 文章結論的思考和尋找真相
- synchronized和volatile的區別synchronized
- volatile與synchronized的區別synchronized
- volatile和synchronized的區別synchronized
- C# VolatileC#
- java volatile 分析Java
- 你說一下對Java中的volatile的理解吧,以及它是怎麼保證可見性的。Java
- volatile的記憶體屏障的坑記憶體
- volatile的特性程式碼驗證
- volatile的語義與實踐
- C++中的mutable和volatileC++
- volatile關鍵字的作用、原理
- 全面解讀volatile和synchronize,輕鬆掌握Volatile與Synchronizedsynchronized
- Generic: volatile — Multithreaded Programmer’s Best Friend Volatile (轉)thread
- 深入理解Java多執行緒與併發框(第⑦篇)——volatile 關鍵字Java執行緒