volatile的可见性问题

本文揭示了关于内存模型和volatile关键字的传统理解误区,指出并非共享变量刷回主存延迟导致线程无法感知变化,而是JIT优化在大量重复读取时进行了优化。通过实例和实验,作者证明了正确的解释是由于循环条件的优化而非可见性问题。
摘要由CSDN通过智能技术生成

我们网上学的基本都是以下结论:

内存模型是一个抽象模型,主要包括了线程私有的本地内存与本地内存里的共享变量副本、主内存共有的共享变量 


可见性问题:当线程对本地共享变量的修改操作,多线程下可能无法保证共享变量及时刷回主存,使得所有其他线程可见(volatile)

package com.special.thread.volatiletest;
 
/**
 * 首先执行main方法的时候;
 *   会创建出一个线程t1;
 *   这个线程t1会来进行读取共享变量flag的取值;
 *   则读取到flag共享变量的取值为true;
 *   那么该while循环则将会一直进行循环;
 *   那么当主线程沉睡了0.1秒之后,结束;
 *   但是控制台显示,t1并没有结束;
 *
 *   如果加上volatile则会结束
 * 
 * 
 */
public class VolatileDemo {
    static volatile boolean flag = false;
 
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (!flag) {
//
            }
        });
 
        t1.start();
        Thread.sleep(1000);
        flag = true;
        System.out.println("main end");
    }
 
}
 

上述说法其实是错误的理论 

被证实是错误的说法:通过循环判断一个值变为TRUE就停止循环,读不到TRUE的原因是因为读取的此处太多次100w,大概一个阈值,使其被JIT优化,导致其不再读取主存的值,默认为FALSE。所以TRUE的改变他获取不到。证实方法:将修改为TRUE的时间加快,睡眠时间设为1,就可以发现他可以正常获取TRUE并停止。(还没有被jit优化

package com.special.thread.volatiletest;
 
public class VolatileDemo {
    static volatile boolean flag = false;
 
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (!flag) {
//               
            }
        });

        Thread t2 = new Thread(() -> {
            Thread.sleep(1000);
            System.out.println(flag);
        });
 
        t1.start();
        t2.start();
        Thread.sleep(1000);
        flag = true;
        System.out.println("main end");
    }
 
}
 

 这时我们开多一个线程,可以发现可以读取到flag的最新的值TRUE,但是线程1循环依旧没有停止。

package com.special.thread.volatiletest;
 
public class VolatileDemo {
    static volatile boolean flag = false;
 
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (!flag) {
//               
            }
        });

        Thread t2 = new Thread(() -> {
            Thread.sleep(1000);
            System.out.println(flag);
        });
 
        t1.start();
        t2.start();
        Thread.sleep(1);
        flag = true;
        System.out.println("main end");
    }
 
}
 

如果我们将主线程的睡眠时间缩短,更快的修改flag的值为TRUE,会发现循环停止了。


所以并不是因为新的值没有及时刷回主存,导致循环无法停止,而是jit优化,因为重复读取了上百万次flag,jvm发现都是同一个值,所以直接对他优化。

        Thread t1 = new Thread(() -> {
            while (!FALSE) {  //直接将flag替换为FALSE,就不会再去读取flag的值
//               
            }
        });

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值