Java多线程 JMM之可见性demo

JMM之可见性demo

如下的代码演示了可见性问题 . 一个线程调用给a 和b赋值的方法, 一个线程调用打印a和b的方法. 并进行循环的打印.

package com.thread.jmm;

/**
 * 类名称:FieldVisibility
 * 类描述:  演示可见性问题
 *
 * @author: https://javaweixin6.blog.csdn.net/
 * 创建时间:2020/9/5 14:31
 * Version 1.0
 */
public class FieldVisibility {

    int a = 1;
    int b = 2;

    //给a 赋值, 并把值给b
    private void change() {
        a = 3;
        b = a;
    }

    /**
     * 打印出a b
     */
    private void print() {
        System.out.println("b=" + b + ";a=" + a);
    }

    public static void main(String[] args) {

        while (true) {
            FieldVisibility test = new FieldVisibility();
            new Thread(()->{
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //给 a b 重新赋值
                test.change();
            }).start();

            new Thread(()->{
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //给 a b 打印出来
                test.print();
            }).start();
        }
    }
}

打印的结果如下图所示, 会出现四种情况:


其中
a=1 b=2
a=3 b=3
这两种情况分别是由于 打印的线程先启动了, 因此打印的是a和b的初始值. 第二种情况是 交换值的线程先启动了, 把a和b的值赋值了,交换了, 所以打印的是3和3 .
打印出a=3 b=2的情况 , 是由于线程的交替执行导致的. 交换的值的线程先执行到了a=3的时候, 还没有把a的值给b , 线程就切换到打印的线程了, 导致b打印的是初始值, 因此打印出了a=3 b=2 .

最后一种情况 a = 1 , b = 3 . 才是由于可见性导致的. 即交换值的线程把a的值改成3了 ,也把3赋值给了b ,但是打印的线程, 由于可见性的原因, b获得的是新值, a却获取的是主内存中的初始值. 因此打印出了 a = 1 , b = 3

可见性问题图解

下图说明了可见性问题的原因.
每个线程从shared cache 主内存中获得数据到自己的内存中(local cache).
线程之间不能直接通信数据, 只能从主内存中获得数据. .
因此会出现如下图所示的, cpu1把x的值改成了1,并且没有把x的最新值同步到主内存 , 而cpu2中 x的值还是0的情况 .

加上volatile 后解决了可见性问题. 不在出现 b=3 a=1的情况

主要的原理如下图 : 加上volatile 后, 只要写的线程修改了变量的值, 就会在读的线程读取x之前, 强制的把修改的x值,刷回到主内存中 .

读的线程, 会自动的把修改后的x的变量的值, load加载到读线程的 内存中, 获取x的最新值.

猜你喜欢

转载自blog.csdn.net/qq_33229669/article/details/108419450