31.volatile保证可见性

JMM-java memory model-java内存模型 

1.原子性,保证指令不受线程上下文切换的影响。

2.可见性,保证指令不会受cpu缓存的影响。

3.有序性,保证指令不会受cpu指令并行优化的影响。

@Slf4j
public class ThreadSee {

    static boolean flag = true;
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while(flag) {
            }
        }, "t1");
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("退出t1线程");
        flag =false;
    }
}

主线程执行flag=false后,子线程t1并没有停下来,为啥?

因为t1线程要频繁的从主内存中读取flag的值,JIT编译器会将flag的值缓存到自己工作内存中的高速缓存中,减少对主内存flag的访问,提高效率。1秒之后,main线程修改了flag的值并同步至主存,而t1线程永远从自己工作内存中的高速缓存中读取这个变量的值,结果永远是旧值。

一个线程对主存中的数据进行了修改,而其他线程不可见,导致了问题。

解决办法:是给变量flag添加volatile修饰。volatile表示易变的关键字。

volatile static boolean flag = true;

问题:如果不使用volatile关键字,然后循环中添加日志打印代码while(flag){log.info("111")},这样也能让t1线程停下来。使用System.out.println()方法也可以让t1线程停下来。

原因:是因为println方法内部其实使用了Synchronized关键字,所以保证的线程的可见性。

使用volatile关键字后,t1线程每循环一次就会从主内存中获取最新的值。

volatile可以用来修饰成员变量和静态成员变量。避免线程从自己的工作缓存中查找变量的值,线程操作volatile变量都是直接操作主存。

volatile修饰的都是多个线程共享的变量,不会修饰局部变量,因为局部变量每个线程都有一份。

volatile可以保证变量的可见性

Synchronized也可以保证共享变量的可见性

@Slf4j
public class ThreadSee {
    static Object lock = new Object();
    static boolean flag = true;
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            while(true) {
                synchronized (lock) {
                    if(!flag) {
                        break;
                    }
                }
            }
        }, "t1");
        t1.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("退出t1线程");
        synchronized (lock) {
            flag = false;
        }
    }
}

synchronized要创建monitor,属于比较重量级的操作,volatile相比较就更轻量,所以在解决可见性方面推荐使用volatile。

volatile只能保证可见性,不能保证原子性。适合用于一个线程修改,多个线程读的情况。

synchronized既可以保证原子性又可以保证可见性。但是属于重量级操作,性能相对更低。

 volatile可见性、有序性的原理

底层实现原理是内存屏障,memory barrier

对volatile变量的写指令后会加入写屏障,写屏障之前的所有赋值操作都会同步到主内存中,哪怕变量没有被volatile修饰,也会同步到主内存中。保证写屏障之前的代码不会发生指令重排。

对volatile变量的读指令后会加入读屏障,读屏障之后对共享变量的读取,加载的是主存中最新数据。保证读屏障之后的代码不会发生指令重排序。

volatile不能解决指令交错,有序性是保证了本线程内相关代码不被重排序。

volatile只能保证可见性、有序性。

synchronized保证原子性、可见性和有序性。

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

卷土重来…

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值