认识volatile

两大特性:线程之间可见性 与  禁止指令重排序

线程之间可见性:

     线程操作共享的主内存中资源都是先将其从主内存读取到自己的工作内存中(正常情况下各线程的工作内存之间是相互隔离的、不可见的),操作完成后再写回至主内存中,在中途一个线程修改了该资源写回到主内存后,其他线程不会知道,还是会依然使用第一次读到工作内存中的资源进行操作。添加volatile关键字,会实现线程之间操作的可见性,也就是每个线程发生每次操作后写回到主内存中后,cpu总线嗅探机制嗅探到工作内存与主内存中数据出现不一致情况,会使工作内存中该不一致数据实现,在下次使用该资源时会第一时间去主内存中再读取一次新值进行使用,实现了线程之间的可见性。

JMM:java内存模型,每个线程有自己的工作内存,工作内存中存放一份主内存数据副本,对副本操作后再写入主内存。但会导致缓存一致性问题

MESI:缓存一致性协议。

禁止指令重排序:

cpu在执行指令的时候为了提高效率,会将先后执行不影响最终结果的指令进行重排序,存在后边指令先执行的情况,但重排序在多线程的情况下有可能会出现问题,比如多个线程相互依赖某些值,但发生了指令重排序导致依赖的值不准确,可以使用volatile关键字禁止指令重排序。

以一个DCL(double check lock)的懒汉式单例设计模式为例:

public class User{
	
	// volatile禁止指令重排
    private volatile static User user;

    // 双重检验锁 DCL 懒汉式单例
    public static User getInstance() {
        if (user == null) {
        	// 加锁
            synchronized (User.class) {
                if (user == null) {
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 创建对象
                    user = new User();
                    // 创建对象分为三个步骤 1.分配堆内存(半初始化状态) 
                                          2.初始化数据
                                          3.栈中变量指向堆中地址
                    // 若无volatile修饰,发生指令重排序,上面2.3步骤颠倒执行,
                    // 此时刚好另一个线程判断user != null,则该线程获取了半初始化状态的user对象

                }
            }
        }
        return user;
    }

volatile实现:

代码中添加volatile关键字,在编译时会添加ACC_VOLATILE标识,jvm执行时看到该标识后会添加jvm内存读写屏障来禁止指令重排(屏障两边的指令不能重排序)。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Self-impr

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

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

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

打赏作者

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

抵扣说明:

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

余额充值