前言
本文章,讓我們一起來探討volatile關鍵字的作用。不知大家有沒有發現沒有,平時在練習或者學習Java編程時,volatile關鍵字幾乎沒有用到過,而平時在看開源項目時,經常會看某些類變量定義為volatile,今天我們來解釋volatile的重要性。
閱讀導航
一、volatile關鍵字的作用
二、volatile如何保證可見性?
三、volatile源碼剖析
四、原子性
五、CAS
volatile關鍵字,是用來在多處理器開發中保證共享變量的「可見性」。也就是說當一個線程修改一個共享變量值時,其他所有線程就能立刻獲得修改以後的值。
二、是如何保證可見性?
volatile進行寫操作時,CPU會做什麼事情呢?又如何保證可見性?
當volatile變量修飾的共享變量進行寫操作的時候,JVM就會向處理器發送一條Lock前綴的指令,然後將這個變量所在緩存行的數據寫回到系統內存,為了保證各個處理器的緩存是一致,這時候就會實現緩存一致性協議。
實現緩存協議是指每個處理器通過在總線上傳播的數據來檢查自己緩存的值是否過期,當發現自己緩存行對應的內存地址被修改,則將緩存行設置成無效狀態;當處理器對這個數據進行修改操作的時候,會重新從系統內存中把數據讀到處理器緩存裡頭。
JVM是如何在JMM層面解決原子性、有序性、可見性問題的呢
原子性:Java中提供了兩個高級指令 monitorenter和 monitorexit,也就是對應的synchronized同步鎖來保證原子性。可見性:volatile、synchronized、final都可以解決可見性問題有序性:synchronized和volatile可以保證多線程之間操作的有序性,volatile會禁止指令重排序三、源碼剖析
編寫一個演示案例來分析volatile
查看字節碼,當被volatile修飾的關鍵字,會多一個ACC_VOLATILE
四、什麼是原子性
多核CPU架構下,在同一時刻對同一共享變量執行decl指令這個指令涉及到兩次內存操作,這就是原子性問題,在32處理器,提供了兩種鎖的機制來保證原子性。
使用總線鎖保證原子性使用緩存鎖保證原子性
五、什麼是CAS
CAS是無鎖優化,或者叫自旋。它的基本操作是循環進行CAS操作來保證線程安全的。
但是CAS實現原子操作時有三大問題
ABA問題:因為CAS需要在操作值的時候,檢查值有沒有發生變化,如果沒有發生變化則更新。解決思路就是在變量前面使用版本號,每次變量更新的時候把版本號加1即可。(Java 1.5開始,JDK的Atomic包裡提供了一個類AtomicStampedReference來解決ABA問題)循環時間長開銷大:自旋CAS如果長時間不成功,會給CPU帶來非常大的執行開銷。只能保證一個共享變量的原子操作:也就是說當對多個共享變量操作時,循環CAS就無法保證操作的原子性。解決方案:可以使用鎖、可以把多個變量放在一個對象裡來進行CAS操作。