併發系列(三)-----volatile

一 簡介

    volatile關鍵字是輕量級的synchronized,volatile在併發編程中保證共享變量的可見性,當一個線程修改被volatile修飾的共享變量時,另一個線程就能讀到這個修改的值。volatile能夠保證共享變量的可見性但不能保證複合操作的原子性:好比像i++這樣的操作是volatile是不能保證的。編程

二 volatile變量的特性

被volatile修飾的變量有兩種特性安全

1.保證此變量對全部線程的可見性,可見性是指當一條線程修改了這個變量的值,新的值對於其它是能夠當即的得知的。雖然volatile對變量有可見性的特色,可是volatile並不能保證volatile++這樣的操作在併發下是安全的。在深刻理解Java虛擬機裏面做者從字節碼的角度解釋了不安全的緣由。具體的描述看書就能夠了。下面是個人理解,volatile保證的變量讀和寫(對於不理解volatile的讀寫的內存語義的下面會講到)的時候是安全的可是並不能保證對變量的操作是線程安全。什麼意思呢,就是在i++的這樣的操作JVM不僅是從內存(指的是JMM的內存)中獲取值和存入值還作了其餘的操作,好比iadd,當它執行像iadd這樣的操作時其餘線程可能將值已經修改了,而此時當前線程是沒有執行獲取操作的因此在當前線程操做的仍是舊值。對比JMM理解會比較容易理解一點。併發

2.禁止指令重排序關於重排序的問題看上一篇文章JMM。線程

三 volatile內存語義

1.volatile的讀寫內存語義

volatile寫的內存語義: 當寫一個volatile變量時,JMM會把該線程對應的本地內存中的共享變量值刷新到主內存3d

volatile讀的內存語義: 當讀一個volatile變量時,JMM會把該線程對應的本地內存置爲無效。線程接下來將從主內存中讀取共享變量blog

2.volatile的內存語義的實現

爲了實現volatile的內存語義,編譯器在生成字節碼時,會在指令序列中插入內存屏障來禁止特定類型的處理器重排序。對於編譯器來講,發現一個最優佈置來最小化插入屏障的總數幾乎不可能。爲此,JMM採起保守策略。下面是基於保守策略的JMM內存屏障插入策略排序

在每一個volatile寫操做的前面插入一個StoreStore屏障。內存

在每一個volatile寫操做的後面插入一個StoreLoad屏障。文檔

在每一個volatile讀操做的後面插入一個LoadLoad屏障。編譯器

在每一個volatile讀操做的後面插入一個LoadStore屏障。

如圖

 


 

 

上圖中的StoreStore屏障能夠保證在volatile寫以前,其前面的全部普通寫操做已經對任意處理器可見了。這是由於StoreStore屏障將保障上面全部的普通寫在volatile寫以前刷新到主內存。

這裏比較有意思的是,volatile寫後面的StoreLoad屏障。此屏障的做用是避免volatile寫與後面可能有的volatile讀/寫操做重排序。由於編譯器經常沒法準確判斷在一個volatile寫的後面是否須要插入一個StoreLoad屏障(好比,一個volatile寫以後方法當即return)。爲了保證能正確實現volatile的內存語義,JMM在採起了保守策略:在每一個volatile寫的後面,或者在每一個volatile讀的前面插入一個StoreLoad屏障。從總體執行效率的角度考慮,JMM最終選擇了在每一個volatile寫的後面插入一個StoreLoad屏障。由於volatile寫-讀內存語義的常見使用模式是一個寫線程寫volatile變量,多個讀線程讀同一個volatile變量。當讀線程的數量大大超過寫線程時,選擇在volatile寫以後插入StoreLoad屏障將帶來可觀的執行效率的提高。從這裏能夠看到JMM在實現上的一個特色:首先確保正確性,而後再去追求執行效率。

參考文檔:

<< Java併發編程藝術>>方騰飛 魏鵬 程曉明

<<深刻理解Java虛擬機:JVM高級特性與最佳實踐>>周志明

相關文章
相關標籤/搜索