Volatile关键字

一、为什么存在可见性问题?

可见性:当一个线程修改了一个数值后,其他线程应该立马看到。

为什么会存在该问题呢?

由于现在的计算机都拥有多个CPU,每个CPU都拥有L1、L2缓存,同时共享L3缓存,L3和内存进行交互。所以,有些时候,CPU之间由于缓存的存在,会有一些数据不一致。

为了保证CPU缓存之间数据的一致性,很多缓存一致性协议就被提了出来,例如MESI协议。MESI总结起来就是每个CPU都会通过嗅探在总线上传播的数据来检查自己的数据是否过期了,如果过期了,则将自己的数据设置为Invalid,下次使用时,强制从内存读取。

但是,缓存一致性协议使得性能下降了。所以,CPU设计者又进行了各种优化,比如在CPU和L1缓存之间加入了各级寄存器。所以,当CPU更改了一个数据后,会先写入寄存器(Buffer),然后由寄存器异步写入L1缓存(存在可见性问题),然后L1缓存同步写入主内存(由MESI协议保证)。

所以,还是会存在一些内存可见性的问题。

二、重排序与内存可见性的关系?

首先,我们先看一下重排序的种类:

  • 编译器重排序:对没有依赖关系的语句,编译器重新调整语句的执行顺序。
  • CPU指令重排序:在CPU指令级别,让没有依赖关系的指令并行
  • CPU内存重排序:由于CPU有自己的buffer和缓存,由于异步写入,造成指令的执行顺序与写入内存的顺序不完全一致。

所以,重排序是在多线程条件下造成了内存可见性问题。

三、as-if-serial语义

因此,我们在什么时候可以重排序?什么时候不可以呢?

在单线程条件下,不管如何重排序,CPU和编译器都会保证程序的运行结果和我们预想的一样,即代码看起来像完全穿行的执行的,这就是as-if-serial语义。

但是,在多线程条件下,CPU和编译器只能保证每个单线程的as-if-serial语义,但是当现成之间存在交互的时候或者访问同一个变量的时候,就需要我们在CPU和编译器之上来决定何时可以重排序,什么时候不可以。

而我们是如何来告知CPU和编译器何时进行重排序,何时不进行的呢? 那就是通过happen-before语义来实现的。

四、happen-before

happen-before描述了两个操作之间的可见性。例如A happend-before B,意味着A的执行结果一定对B可见。这并不意味着A一定比B先执行,而是如果A比B早执行了,则A的结果一定对B可见。如果B先执行了,产生的错误需要我们去同步解决。

五、happen-before的实现(内存屏障)

happen-before的实现原理即借助内存和编译器提供的内存屏障。

cpu层面的内存屏障:

  • LoadLoad:禁止读读重排
  • StoreStore:禁止写写重排
  • LoadStore:禁止读和写重排
  • StoreLoad:禁止写和读的重排

java的unsafe()类中提供了三种内存屏障:

  • loadFence:LoadLoad + LoadStore
  • storeFence:StoreStore + LoadStore
  • fullFence:四种加在一起

六、Volatile的实现原理

在volatile写操作之前加一个StoreStore屏障,保证该变量写的串行化,在后面加一个StoreLoad屏障,保障后面的读操作读取的是最新的值。

在volatile读操作之后加一个LoadStore屏障和一个LoadLoad屏障,保证读操作不会和后面的读操作和写操作不会发生重排序。

通过java并发编程(4)-----内存模型这篇文章我们知道,CPU的MESI模型是各个核通过嗅探来检查自己的缓存中的数据是否过期,但是虽然嗅探到了数据过期了,但是CPU由于存在指令重排,所以导致后面的指令到前面去,此时另一个CPU更新的数据还未写回主存,当前CPU则从自己缓存行中取了数据,导致并发错误。所以内存屏障的含义是配合缓存一致性协议,保证各个CPU数据的可见性。挡住当前CPU的指令,防止指令重排,以等待别的CPU发来消息,将自己缓存行中对应的数据设置为Invalid。

还是不太懂。。。等以后有机会再找找有没有参考书吧
有个问题,如果两个CPU同时修改了一个数据呢??这时候该怎么办?

参考

在这里插入图片描述

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值