Volatile可見性代碼驗證

Volatile可見性是指:在JMM模型中,所有的線程操作數(shù)據(jù)時,都不能直接操作主內(nèi)存里的數(shù)據(jù),都需要將數(shù)據(jù)復(fù)制一份到線程內(nèi)存中,只能修改線程內(nèi)存中的數(shù)據(jù)(詳細可以查找JMM相關(guān)知識)。然而,在一個線程中修改了某一個變量的值之后,應(yīng)該立即將線程內(nèi)存中修改的值,同步到主內(nèi)存中,并通知其他線程,讓其他線程重新獲取變量值。

1、正確的樣例代碼

1、 首先定義一個數(shù)據(jù)類
這里有兩個變量,一個用volatile修飾,另一個沒有;

public class MyData {

    int number1 = 0;
    volatile int number2 = 0;

    public void add(){
        this.number1 = 11;
        this.number2 = 22;
    }
}

2、 新建一個測試類,內(nèi)容如下,

import java.util.concurrent.TimeUnit;

public class ViewableDemo {

    public static void main(String[] args) {
       MyData myData = new MyData();
       new Thread(() -> {
           System.out.println(Thread.currentThread().getName() + " come in ...");
           try {
               TimeUnit.SECONDS.sleep(1);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }

           myData.add();

           System.out.println(Thread.currentThread().getName() + " update number value to:" + myData.number2);
       },"AA").start();

        // 此處,如果循環(huán)判斷的時number1,沒有加volatile修飾,就會一直死循環(huán)下去,
        // 但是如果沒有這個循環(huán),等待2s之后直接獲取,則能獲取到線程A改變后的值,因為,主線程,是在線程A修改之后才拿到的數(shù)據(jù);
       while(myData.number2 == 0){
           // 如果數(shù)字一直沒變就死循環(huán)
       }
        System.out.println(Thread.currentThread().getName() + " get value " + myData.number2);
    }

}

3、 程序最終運行結(jié)果如下:

image

2、錯誤示例

一開始我認為,如果沒有加volatile修飾,那么線程內(nèi)修改變量,對其他線程就是不可見的,于是我寫了如下代碼:

public class VolatileTest {
    public static void main(String[] args) throws InterruptedException {
        MyData myData = new MyData();
        new Thread(()-> {
            System.out.println(Thread.currentThread().getName() + " come in");
            myData.add();
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " finish");
        },"thread111").start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(myData.number1);
        System.out.println(myData.number2);
    }

}

我一開始以為的運行結(jié)果,應(yīng)該是number1=0,number2=22,結(jié)果卻是這樣:

image

可見,不論有沒有加volatile修飾,主線程都獲取到了修改后的值,這個是為什么呢?
和正確的驗證代碼對比我們不難發(fā)現(xiàn),正確樣例中,主線程在線程A修改數(shù)字之前,就已經(jīng)拿到了數(shù)據(jù),并且一直占用著,所以線程A修改數(shù)字number1之后,因為沒有volatile修飾,所以一直拿不到修改后的值,導(dǎo)致一直死循環(huán),所以寫了以下代碼驗證:

public class VolatileTest {
    public static void main(String[] args) throws InterruptedException {
        MyData myData = new MyData();

        new Thread(()-> {
            System.out.println(Thread.currentThread().getName() + " come in");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myData.add();
            System.out.println(Thread.currentThread().getName() + " change number value");
        },"AAA").start();


        new Thread(()-> {
            while(myData.number1 == 0){

            }
            System.out.println(Thread.currentThread().getName() + " number2 value is " + myData.number1);
        },"BBB").start();

        new Thread(()-> {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + " number2 value is " + myData.number1);
        },"CCC").start();
    }
}

程序的運行結(jié)果為:

image

結(jié)果可見:線程BBB,因為在線程A修改之前就拿到了數(shù)據(jù),所以一直認為number1數(shù)字是0,陷入了死循環(huán);而線程C,在線程A修改之后,才去取值,取到的時修改后的值。

3、結(jié)論

沒有volatile修飾的變量,在修改之后也會同步到主內(nèi)存中,但是如果其他線程在此之前已經(jīng)取走了數(shù)據(jù),不會通知其他線程修改,加了volatile后,會在主內(nèi)存數(shù)據(jù)發(fā)生變化之后,通知其他所有線程,來重新拿新數(shù)據(jù)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容