volatile保證可見性的驗證

一、前言

    java內存模型中的可見性是指,當一個線程修改了共享變量的值後,其他線程可以立馬知道這個修改後的值。在《Java併發編程:volatile關鍵字解析》中有這麼一段話:

  對於可見性,Java提供了volatile關鍵字來保證可見性。

  當一個共享變量被volatile修飾時,它會保證修改的值會立即被更新到主存,當有其他線程需要讀取時,它會去內存中讀取新值。

  而普通的共享變量不能保證可見性,因爲普通共享變量被修改之後,什麼時候被寫入主存是不確定的,當其他線程去讀取時,此時內存中可能還是原來的舊值,因此無法保證可見性。

    對於這一論點,網上鮮有論據。所以,筆者通過幾個簡單的例子來證明volatile確實可以保證可見性。

二、論證過程

    每個線程都有一個工作內存,線程的工作內存中保存了被該線程使用到的變量的主內存副本拷貝,線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存中的變量。不同線程之間也無法直接訪問對方工作內存中的變量,線程間變量的傳遞依靠主內存來完成。
在這裏插入圖片描述
    以此原理,設計瞭如下的例子:

package ThreadPool;

public class VolatileTest {
    public boolean isShutdown;

    public boolean getShutdown () {
        return isShutdown;
    }

    public void shutdown () {
        isShutdown = true;
    }

    public class ReaderThread extends Thread {
        @Override
        public void run() {
            try {
                System.out.println("開始循環");
                while (!isShutdown) {

                };
                System.out.println("結束循環");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    public class WatchThread extends Thread {
        @Override
        public void run() {
            shutdown();
        }
    }
    
    public static void main(String[] args){
        try {
            VolatileTest volatileTest = new VolatileTest();
            volatileTest.new ReaderThread().start();
            //讓主線程睡眠一秒,確保另一個線程調用shutdown方法時死循環已經開始
            Thread.sleep(1000);

            volatileTest.new WatchThread().start();
            //此刻的睡眠是爲了確保shutdown方法對isShutdown變量的修改已經同步到主內存
            Thread.sleep(1000);
            //打印isShutdown的值
            System.out.println("getShutdown:" + volatileTest.getShutdown());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

    isShutdown是共享變量,默認值是false。ReaderThread線程的執行體是一個死循環,直到isShutdown變爲true才跳出循環,打印“結束循環”。WatchThread線程的執行體調用了shutdown方法,將isShutdown的值改爲true。如果說ReaderThread線程跳出了死循環,那麼就說明WatchThread線程對isShutdown的修改是對其可見的,反之,則不可見。
    運行結果如下:
在這裏插入圖片描述
    由此可見,WatchThread對isShutdown變量的修改是生效的,但ReaderThread並沒有跳出循環,也就意味着ReaderThread並沒有讀取到isShutdown變量的最新值,而是讀取工作內存中的舊值。所以,普通變量是不具有可見性的。
    緊接着,我們用volatile去修飾isShutdown變量。
在這裏插入圖片描述
    運行結果如下:
在這裏插入圖片描述
    這一次,ReaderThread跳出了死循環,打印出了“結束循環”,這說明ReaderThread可以讀取到isShutdown變量的最新值,換句話說就是,WatchThread對isShutdown變量的修改對於ReaderThread是可見的。

三、總結

    綜上所述,volatile確實可以保證可見性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章