1. 程式人生 > >volatile關鍵字用法以及執行緒資料可見性的問題

volatile關鍵字用法以及執行緒資料可見性的問題

最近在研究ConcurrentHashMap的原始碼的時候,發現底層實現的Segments使用到了關鍵字volatile

不太明白這個關鍵字的用法,查了一些資料總結如下:

volatile 的作用是讓變數在多個執行緒可見。

說實話這個定義也有點籠統,既然看原始碼了肯定是想知道具體是怎麼讓多執行緒可見的。

實際上Java裡每個執行緒工作的時候,都會有自己的一個執行緒私有工作記憶體,裡面存放著只有本執行緒可見的變數,這些執行緒共同連線了一個的公有記憶體(我理解為Java程式的主記憶體區)。執行緒為了執行的效率,除了首次從公有記憶體獲取資料外,讀取的資料都是本執行緒私有工作記憶體的資料(看到有書上說只有JVM設定-server

引數的時候才會是這樣,不過我本地測試的時候有沒有這個引數測試結果好像都一樣,稍後再查一下這個問題)。

這樣的結果就是私有工作記憶體的值和公有記憶體的值不同步,即使本執行緒修改了一個數值,本執行緒讀取的也可能是錯誤的數值。為了驗證這個問題,編寫實驗程式碼如下:

class TestThread extends Thread{
    private boolean flag = false;  

    public void setFlag(boolean flag){
        this.flag = flag;
    }

    @Override  
    public
void run() { for (;;) { if(flag){ System.out.println("迴圈結束"); System.exit(0); } } } public static void main(String[] args) throws InterruptedException { TestThread a = new TestThread(); a.start(); //加入休眠,保證執行緒執行之後才設定標識
Thread.sleep(10); a.setFlag(true); } }

大家可以執行一下,以上程式碼會死迴圈。

其原因就是我們修改的flag是公共堆疊的flag,而執行緒讀取的則是執行緒私有堆疊的flag,兩個變數沒有同步(主要是a執行緒一直讀取自己的私有堆疊),所以執行緒沒有終止,此時我們給flag變數加上volatile 關鍵字,上述程式執行正常。

    private volatile boolean flag = false;  

總結

綜上,volatile關鍵字的作用是,強制變數每次都讀取公共記憶體,這樣,一旦有執行緒改變變數的值,其他執行緒馬上就能發現。這就是定義裡,所有執行緒可見的含義。
volatile

但是要注意,此關鍵字只能保證變數的可見性,不能保證原子性,也不會阻塞執行緒。也就是說多個執行緒併發修改變數時,還是會有併發性問題。要解決併發性的問題,還是隻能用synchronized關鍵字。應用的場景,就是一旦改動,所有執行緒都能馬上感知到的的情況。例如示例程式碼設定子執行緒停止迴圈的標誌。