访问共享的可变数据

对可变共享数据的访问如果不能同步,可能会出现预料之外的结果。但是下面这个例子就很容易导致误判,且很多帖子上的解释不是那么的准确。

public class StopThread {
    private static boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int i=0;
                while(!stopRequested) {
                    i++;
                }
            }
        });
        thread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}

上面这个例子大家再熟悉不过了,你可能会 觉得这个程序在运行大概1秒钟左右之后,主线程将stopRequested设置为true,使线程thread中的循环终止。但是结果却不会停止。

  • 解决方式一:使用volatile修饰stopRequested变量;
  • 解决方式二:在while循环中增加一行打印System.out.println("xxx"),打印任何东西都可以;
  • 解决方式三:使用同步访问stopRequested;

原因分析:

  1. 方案一大家可能会觉得之所以修改之前的程序不会终止,是因为变量stopRequested,是线程不可见的,主线程修改之后,子线程无法获取修改后的值,其实这个解释是错误的;反例就是解决方案二;
  2. 方案二也可以解决子线程无法终止的问题,可以反向证明stopRequested不是因为子线程不可见导致的;
  3. 其实真正的原因是JVM 将while循环做了转变,即将代码
    while(!stopRequested) {
        i++;
    }

    转变成这样:

    if(!stopRequested) {
        while(true) {
            i++;
        }
    }

    这种优化称作提升(hoisting),这是HotShot JVM的工作模式。方案一中因为加了volatile之后,禁止JVM做指令重排序,不会出现上面的代码转变;方案二中增加打印也是为了禁止JVM进行代码的转变(因为println()方法是synchronized的);

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值