聊聊volatile作用,原理

本文深入探讨了Java中的volatile关键字,它确保了变量在多线程环境中的可见性和禁止指令重排。volatile通过内存屏障来实现其特性,其中lock指令起到了关键作用。文中通过代码示例解释了volatile如何保证并发安全,并分析了带volatile的代码在编译后的汇编指令差异,揭示了其背后的工作原理。
摘要由CSDN通过智能技术生成

volatile关键字是Java虚拟机提供的的最轻量级的同步机制。它作为一个修饰符,用来修饰变量。它保证变量对所有线程可见性,禁止指令重排,但是不保证原子性。

我们先来一起回忆下java内存模型(jmm):

Java虚拟机规范试图定义一种Java内存模型,来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让Java程序在各种平台上都能达到一致的内存访问效果。
Java内存模型规定所有的变量都是存在主内存当中,每个线程都有自己的工作内存。这里的变量包括实例变量和静态变量,但是不包括局部变量,因为局部变量是线程私有的。
线程的工作内存保存了被该线程使用的变量的主内存副本,线程对变量的所有操作都必须在工作内存中进行,而不能直接操作主内存。并且每个线程不能访问其他线程的工作内存。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XBTbUUjC-1661215614276)(https://a.perfma.net/img/4418586#pic_center)]
在这里插入图片描述
volatile变量,保证新值能立即同步回主内存,以及每次使用前立即从主内存刷新,所以我们说volatile保证了多线程操作变量的可见性。
volatile保证可见性和禁止指令重排,都跟内存屏障有关。我们来看一段volatile使用的demo代码:

public class Singleton {  
   private volatile static Singleton instance;  
   private Singleton (){}  
   public static Singleton getInstance() {  
   if (instance == null) {  
       synchronized (Singleton.class) {  
       if (instance == null) {  
           instance = new Singleton();  
       }  
       }  
   }  
   return instance;  
   }  
}  



编译后,对比有volatile关键字和没有volatile关键字时所生成的汇编代码,发现有volatile关键字修饰时,会多出一个lock addl $0x0,(%esp),即多出一个lock前缀指令,lock指令相当于一个内存屏障
lock指令相当于一个内存屏障,它保证以下这几点:

重排序时不能把后面的指令重排序到内存屏障之前的位置
将本处理器的缓存写入内存
如果是写入动作,会导致其他处理器中对应的缓存无效。
第2点和第3点就是保证volatile保证可见性的体现嘛,第1点就是禁止指令重排的体现。
内存屏障四大分类:(Load 代表读取指令,Store代表写入指令)
在这里插入图片描述
在每个volatile写操作的前面插入一个StoreStore屏障。
在每个volatile写操作的后面插入一个StoreLoad屏障。
在每个volatile读操作的后面插入一个LoadLoad屏障。
在每个volatile读操作的后面插入一个LoadStore屏障。
有些小伙伴,可能对这个还是有点疑惑,内存屏障这玩意太抽象了。我们照着代码看下吧:
在这里插入图片描述
内存屏障保证前面的指令先执行,所以这就保证了禁止了指令重排啦,同时内存屏障保证缓存写入内存和其他处理器缓存失效,这也就保证了可见性

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值