记一次内存可见性的深刻感悟

前言

  很多同学都知道,volatile关键字是来保证内存可见性的。那如果变量没有被volatile修饰,它一定是不可见的么?我们来一探究竟。

示例

public class MyVolatile {

    private boolean flag = true;

    public boolean getFlag() {
        return this.flag;
    }

    public void stopThread() {
        System.out.println(Thread.currentThread().getName() + 
        ":stopThread");
        flag = false;
    }

}
public class Test {

    public static void main(String[] args) throws InterruptedException {
        MyVolatile myVolatile = new MyVolatile();
        
        new Thread(() -> {
            //或者for(;;) {}
            while(true) {
                if(!myVolatile.getFlag()) {
                    System.out.println(Thread.currentThread().getName() + 
                    "执行完毕");
                    break;
                }
            }
        }).start();


        Thread.sleep(500);
        myVolatile.stopThread();
    }

}

  看下结果:

在这里插入图片描述

  程序没有终止:说明子线程拿到的myVolatile对象的flag属性,一直为true。此时flag属性是内存不可见的。

  我们把子线程的执行代码修改一下,其余不变:

new Thread(() -> {
    while(myVolatile.getFlag()) {
        System.out.println(Thread.currentThread().getName() + 
        "正在运行。。。");
    }

    System.out.println(Thread.currentThread().getName() + "执行完毕");
}).start();

  运行结果:

在这里插入图片描述

  这里又行了,flag属性又可见了!!!那这是为什么呢?笔者也是查阅了相关资料,第一种while(true)的写法,如果循环体的执行效率非常高的话,执行时就不会再去获取flag属性的最新值了,即不可见了;而第二种写法,在while中加入了判断:flag属性是否为true,所以每次循环前都要去获取flag属性的最新值,此时就是可见的。
  现在把第一种写法的循环体改动一下,不要让它的执行效率那么高:

new Thread(() -> {
    //或者for(;;) {}
    while(true) {
        if(!myVolatile.getFlag()) {
            System.out.println(Thread.currentThread().getName() + 
            "执行完毕");
            break;
        }
		
        try {
            Thread.sleep(100);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

  或者使用同步方法:

Object o = new Object();
new Thread(() -> {
	//或者for(;;) {}
    while(true) {
        synchronized (o) {
            if (!myVolatile.getFlag()) {
                System.out.println(Thread.currentThread().getName() + 
                "执行完毕");
                break;
            }
        }
    }
}).start();

  再看运行结果:

在这里插入图片描述

  此时flag属性是可见的了。但这2种是不是性能就低下了,这时volatile关键字就派上用场了,把它加到flag属性上:

private volatile boolean flag = true;

  子线程再用原先的第一种写法,运行后程序是能终止的。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值