volatile可见性实现原理

        当修改volatile变量时,会给cpu发送一个信号告诉其他cpu这个变量已修改,当其他cpu调用这个变量时,就会先检查是否有收到修改该变量的信号,有则重新从内存中读取。volatile是无锁的,类似于乐观锁的机制。

        在说这个问题之前,我们先看看CPU是如何执行java代码的。首先编译之后Java代码会被编译成字节码.class文件,在运行时会被加载到JVM中,JVM会将.class转换为具体的CPU执行指令,CPU加载这些指令逐条执行。如下图,

java编译过程

 

 

java代码执行过程

        以多核CPU为例(两核),我们知道CPU的速度比内存要快得多,为了弥补这个性能差异,CPU内核都会有自己的高速缓存区,当内核运行的线程执行一段代码时,首先将这段代码的指令集进行缓存行填充到高速缓存,如果非volatil变量当CPU执行修改了此变量之后,会将修改后的值回写到高速缓存,然后再刷新到内存中。如果在刷新会内存之前,由于是共享变量,那么CORE2中的线程执行的代码也用到了这个变量,这是变量的值依然是旧的。volatile关键字就会解决这个问题的,如何解决呢,首先被volatile关键字修饰的共享变量在转换成汇编语言时,会加上一个以lock为前缀的指令,当CPU发现这个指令时,立即做两件事:

  • 将当前内核高速缓存行的数据立刻回写到内存;

  • 使在其他内核里缓存了该内存地址的数据无效。

        第一步很好理解,第二步如何做到呢?使用MESI协议。

        MESI协议在早期的CPU中,是通过在总线加LOCK#锁的方式实现的,但这种方式开销太大,所以Intel开发了缓存一致性协议,也就是MESI协议,该解决缓存一致性的大致思路是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,那么他会发出信号通知其他CPU将该变量的缓存行设置为无效状态。当其他CPU使用这个变量时,首先会去嗅探是否有对该变量更改的信号,当发现这个变量的缓存行已经无效时,会从新从内存中读取这个变量。

        以上这些就是volatile关键字的内部实现机制,使用Volatile有什么好处呢?从底层实现原理我们可以发现,volatile是一种非锁机制,这种机制可以避免锁机制引起的线程上下文切换和调度问题。因此,volatile的执行成本比synchronized更低。

        MESI(缓存一致性协议)是一种保持一致性的协议。它的方法是在CPU缓存中保存一个标记位,这个标记位有四种状态:

  • M:Modify,修改缓存,当前CPU的缓存已经被修改了,即与内存中数据已经不一致了

  • E:Exclusive,独占缓存,当前CPU的缓存和内存中数据保持一致,而且其他处理器并没有可使用的缓存数据

  • S:Share,共享缓存,和内存保持一致的一份拷贝,多组缓存可以同时拥有针对同一内存地址的共享缓存段

  • I:Invalid,实效缓存,这个说明CPU中的缓存已经不能使用了

        CPU的读取遵循下面几点:

  • 如果缓存状态是I,那么就从内存中读取,否则就从缓存中直接读取。

  • 如果缓存处于M或E的CPU读取到其他CPU有读操作,就把自己的缓存写入到内存中,并将自己的状态设置为S。

  • 只有缓存状态是M或E的时候,CPU才可以修改缓存中的数据,修改后,缓存状态变为M。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值