Volatile總結

volatile被視作是輕量級的sychronized。與sychronied關鍵字比較,volatile只能保證共享變量數據的可見性,即,當一個變量被多個線程共享,可修改時,一個線程的修改結果會立刻對其他線程可見。

volatile是如何做到可見性的?

首先,要明白為什么會有可見性問題。

CPU負責數據處理,實際的運行時數據存儲在內存中,CPU和內存之間通過總線傳遞數據。由于CPU和內存對數據處理的速度有很大差異,所以通常CPU存取的數據都不會直接與內存交互,轉而經過處理器緩存讀寫。這樣的直接后果是,處理器緩存內的數據和內存中的數據未必一致,無法保證最新被處理過的結果立刻刷新到內存中去,同樣也無法保證讀取到的就是最新被刷新到內存的結果,可見性問題由此產生。類似于這種描述,Java在線程通信方面,采用的是共享內存模型,線程通過共享狀態,隱式通信。從下圖看,線程A、B之間的通信,需要先把本線程內的共享變量副本刷新到主存,然后經由另一個線程再去將主內存中的變量值同步到該線程本地內存中。

image

此外,處理器的執行順序和內存中的順序不會一致,雖然從性能優化的角度考量,這是很合理的,但是會對程序員編程造成一定的影響。如何保證內存編程的結果和想要的一致,需要介于程序員和處理器之間建立約定。

image

上圖引用自《Java并發編程藝術》一書,很好的闡釋了程序員與處理器之間的矛盾,程序員希望很好的控制程序運行,程序按定義語義執行,處理器則希望盡可能的提高效率,盡量不受執行順序約束。為了應對二者之間的矛盾,需要給上層的程序編寫者提供一個很強的保證,即按照該保證編程,就能得到想要的執行結果(在此并不能完全承諾執行順序和保證的順序一致)。這個“保證”就是“happens-before”規則。

它承諾:

(1)程序順序:單線程內的每個操作,happens before該線程內的后續操作;

(2)鎖:解鎖操作,happens before隨后對這個鎖的加鎖;

(3)volatile:volatile域的寫,happens before后續對這個域的讀;

(4)傳遞性:a happens before b,b happens before c,則有a happens before c。

程序員只要按照這個原則編程,就可以保證處理器執行結果。

那么volatile是如何實現happens before的效果?也就是有效阻止某些處理器的重排序,答案是內存屏障(P26)。一旦域被volatile修飾,編譯后的字節碼內會被添加上各種內存屏障。同樣加上其他,如synchronized和final原語也會添加這種屏障。JMM把內存屏障分為四類,即LoadLoad、StoreStore、LoadStore和StoreLoad屏障,其中StoreLoad具有其他三種的所有效果,被現代處理器廣泛支持。

StoreLoad Barriers指令示例為Store1;StoreLoad;Load2,確保Store1數據對其他處理器變得可見(即刷新到內存)先于Load2及所有后續裝載指令的裝載。StoreLoad Barriers會使該屏障前的所有內存訪問指令(存儲和裝載指令)完成之后,才執行該屏障之后的內存訪問指令。

關于volatile實現原理,可以參考《Java并發編程藝術》P39-47。文章主要闡述的就是,寫-讀的內存語義,以及volatile是如何針對這種場景添加內存屏障,保證程序員看到的“happens before”原則視圖有效的。復習時可以看看,找找感覺。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容