1. 程式人生 > >Java Volatile關鍵字解析

Java Volatile關鍵字解析

       在多執行緒併發程式設計中synchronized和volatile都扮演者重要角色。volatile是輕量級的synchronized,它在多處理器開發中保證了共享變數的“可見性”。

一、volatile的特性

    記憶體可見性:執行緒A對一個volatile變數的修改,對於其他執行緒是可見的,即執行緒獲取volatile變數的值都是最新的。

二、實現原理相關說明

    記憶體屏障(memory barriers)是一組處理器指令,用於實現對記憶體操作的順序限制。

    緩衝行(cache line)CPU快取記憶體中可以分配的最小儲存單位。處理器填寫快取行時,會載入整個快取行。

    原子操作(atomic operations)

不可中斷的一個或一系列操作。    

三、如何保證記憶體可見性

    在X86處理器下通過工具獲取JIT編譯器生成的彙編指令來檢視對volatile寫操作時的情況。

    Java程式碼

instance = new Singleton();//instance是volatile變數

    轉變為彙編程式碼

0x01a3deld:movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);

    有volatile變數修飾的共享變數進行寫操作時會多一行彙編程式碼,其中有lock關鍵詞,Lock字首指令在多核處理器下會引發兩件事情:

  1. 將當前處理器快取行的資料寫回到系統記憶體。
  2. 這個寫回操作會使在其他CPU裡快取了該記憶體地址的資料無效。

    為了提高處理速度,處理器不直接和記憶體進行通訊,而是先將系統記憶體的資料讀到內部快取後再進行操作,但是操作完不知道何時寫到記憶體中。

                        

    如果對聲明瞭volatile變數進行寫操作,JVM就會向處理器傳送一個Lock字首指令,將這個變數所在的快取行的資料寫回到系統記憶體。但其他處理器快取的值還是舊的,所有在多處理器環境下,為保證各個處理器的快取是一致的,就回實現快取一致性協議,每個處理器通過嗅探在總線上傳播的資料來檢查自己快取的值是不是過期了,當處理器發現自己快取行對應的記憶體地址被修改,就會將該快取行設定為無效狀態

,當處理器對這個資料進行操作時就會重新從記憶體中讀取資料到快取中。

                            

四、volatile實現原則

    1.Lock字首指令會引起處理器快取寫回到記憶體。

    2.一個處理器的快取寫回到記憶體會導致其他處理器的快取無效。

五、volatile的使用場景

    通過關鍵字sychronize可以防止多個執行緒進入同一段程式碼,在某些特定場景中,volatile相當於一個輕量級的sychronize,因為不會引起執行緒的上下文切換,但是使用volatile必須滿足兩個條件:

  1. 對變數的寫操作不依賴當前值,如多執行緒下執行a++,是無法通過volatile保證結果準確性的;
  2. 該變數沒有包含在具有其它變數的不變式中,這句話有點拗口,看程式碼比較直觀。

    在專案中經常會用到volatile關鍵字的兩個場景:

    1、狀態標記量

    在高併發環境中,通過一個boolean型別的變數 flag,控制程式碼是否走其他邏輯。

public class Handler {
    private volatile flag; 
    public void setFlag(boolean flag) {
        this.flag = falag;
    }
    public void run() {
        if (flag) {
           //其他邏輯
        } else {
          //正常邏輯
        }
    }
}

使用者的請求執行緒執行run方法,如果需要開啟其他活動,可以通過後臺設定,具體實現可以傳送一個請求,呼叫其他方法並設定flag為true,由於flag是volatile修飾的,所以一經修改,其他執行緒都可以拿到flag的最新值,使用者請求就可以執行其他邏輯了。

    2.雙檢鎖/雙重校驗鎖(DCL,即 double-checked locking)

    單例模式的一種實現方式,如果沒有volatile關鍵字,會出現bug。

  1.  volatile關鍵字可以保證jvm執行的一定的"有序性",在指令1和指令2執行完之前,指定3一定不會被執行。(禁止重排序)
  2.  保證了volatile變數被修改後立刻重新整理會駐記憶體中。
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  


參考資料:

-《Java併發程式設計的藝術》 方騰飛 魏鵬 程曉明 著