0
点赞
收藏
分享

微信扫一扫

多线程无锁情况下对共享变量的可见性分析

生活记录馆 2022-03-11 阅读 51

先把代码截图,再看下文

// 建议可以代码截图,然后配合文字一起看
public class Test {
    static long a= 0; //共享变量
    public static void main(String[] args) throws Exception {
        Thread work = new Thread(() -> {
            while (a== 0) {}
        });
        work.start(); //工作线程开启
    }
}

        代码里的变量间的关系如下图,a 是共享变量,在主存,work线程的工作内存有一个主存共享变量a的副本 a1, main线程的工作内存有一个主存共享变量a的副本 a1,线程只能操作自己内存的副本a。

        当work的 while循坏的代码块被 JIT激进优化时,由于激进优化的代码块内有个共享变量,所以,此时,a1=a 这条线断开,即副本a1 无法被主存更新了,但是,无论JIT优化开启与否,a=a1这条线是一定存在的,即工作内存一定可以将自己的副本a1的值赋值给主存的变量a,这个逻辑是恒成立的。

再来看看这段代码

public class Test {
    static long a = 0; //共享变量
    public static void main(String[] args) throws Exception {
        new Thread(() -> {
//            System.out.println(new Date());
            for (long i = 0; i < 1000000000l; i++) {a = i;} // 循环块1
//            System.out.println(new Date());
            for (long i = 0; i < 9000000000l; i++) {}// 循环块2
//            System.out.println(new Date());
            System.out.println(a); //输出为0
        }).start();
        Thread.sleep(1000 * 6);//
        a = 0;
    }
}

        分析代码:在匿名线程启动后,循环块1经过反复的循环,所以这段代码会触发JIT即时编译,如果看我上篇的文章就知道,在线程里触发JIT的代码块,如果代码块有共享变量,那么这个线程可能就无法 感受到其他线程对于这个共享变量的修改!按照这个逻辑来看这段代码

        首先开启匿名线程,然后主线程等待6秒,这个6秒是在 我的电脑 上面选的一个特定的值,能确保这个时候main线程对共享变量a修改时,匿名线程在循环块2中执行,按照之前的逻辑,匿名线程在 循环块1 执行时,就触发了即时编译,那么对于匿名线程的副本共享变量a来说,应该无法被主存的共享变量a 更新,所以我在main线程里 修改了 main线程的副本共享变量a ,然后main线程的副本共享变量a 再更新给主存里的a,更新后 主存的a应该无法再更新给匿名线程了,所以,按照道理,这里输出的应该是 999999999,但是结果确实 0,难道我之前的结论都是错的吗?其实问题在于,循环块1 里共享变量a只有写入,而没有读取操作,我们之前用a++,得先读取了共享变量,然后再写入值。所以对于这种只有写入的,线程工作内存的副本还是能与主存保持 同步,其实,只有代码块内发生对共享变量的读取,并且代码块被JIT 激进优化后,对于发生读取操作的这个共享变量,在线程内是无法和主存保持同步。

上面的图应该可以很容易理解的

举报

相关推荐

0 条评论