Volatile可见性代码验证

Volatile可见性是指:在JMM模型中,所有的线程操作数据时,都不能直接操作主内存里的数据,都需要将数据复制一份到线程内存中,只能修改线程内存中的数据(详细可以查找JMM相关知识)。然而,在一个线程中修改了某一个变量的值之后,应该立即将线程内存中修改的值,同步到主内存中,并通知其他线程,让其他线程重新获取变量值。

1、正确的样例代码

1、 首先定义一个数据类
这里有两个变量,一个用volatile修饰,另一个没有;

public class MyData {

    int number1 = 0;
    volatile int number2 = 0;

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

2、 新建一个测试类,内容如下,

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();

        // 此处,如果循环判断的时number1,没有加volatile修饰,就会一直死循环下去,
        // 但是如果没有这个循环,等待2s之后直接获取,则能获取到线程A改变后的值,因为,主线程,是在线程A修改之后才拿到的数据;
       while(myData.number2 == 0){
           // 如果数字一直没变就死循环
       }
        System.out.println(Thread.currentThread().getName() + " get value " + myData.number2);
    }

}

3、 程序最终运行结果如下:

image

2、错误示例

一开始我认为,如果没有加volatile修饰,那么线程内修改变量,对其他线程就是不可见的,于是我写了如下代码:

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);
    }

}

我一开始以为的运行结果,应该是number1=0,number2=22,结果却是这样:

image

可见,不论有没有加volatile修饰,主线程都获取到了修改后的值,这个是为什么呢?
和正确的验证代码对比我们不难发现,正确样例中,主线程在线程A修改数字之前,就已经拿到了数据,并且一直占用着,所以线程A修改数字number1之后,因为没有volatile修饰,所以一直拿不到修改后的值,导致一直死循环,所以写了以下代码验证:

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();
    }
}

程序的运行结果为:

image

结果可见:线程BBB,因为在线程A修改之前就拿到了数据,所以一直认为number1数字是0,陷入了死循环;而线程C,在线程A修改之后,才去取值,取到的时修改后的值。

3、结论

没有volatile修饰的变量,在修改之后也会同步到主内存中,但是如果其他线程在此之前已经取走了数据,不会通知其他线程修改,加了volatile后,会在主内存数据发生变化之后,通知其他所有线程,来重新拿新数据。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容