volatile

作者: 7d972d5e05e8 | 来源:发表于2020-06-28 23:25 被阅读0次

一、volatile可见性和禁止重排序是怎么实现的?

首先从JMM内存模型层面,volatile通过storestore,storeload,loadload,loadstore四个定义好的内存屏障来实现的。

原文链接:https://blog.csdn.net/Xiewanru/article/details/97647184

volatile通过插入读屏障和写屏障保证可见性,在volatile禁止重排序上,也是通过内存屏障实现的。
因为内存屏障可以使一些指令按照特定顺序执行。
volatile禁止指令重排序的规则:
 1.当第二个操作是voaltile写时,无论第一个操作是什么,都不能进行重排序
 2.当地一个操作是volatile读时,不管第二个操作是什么,都不能进行重排序
 3.当第一个操作是volatile写时,第二个操作是volatile读时,不能进行重排序

用人话就是下面的解释:
下面是基于保守策略的JMM内存屏障插入策略:
在每个volatile写操作的前面插入一个StoreStore屏障。
在每个volatile写操作的后面插入一个StoreLoad屏障。
在每个volatile读操作的前面插入一个LoadLoad屏障。
在每个volatile读操作的后面插入一个LoadStore屏障。

内存屏障,又称内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。

内存屏障可以被分为以下几种类型

  1. LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
  2. StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
  3. LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
  4. StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。

参考文章:
Java并发:volatile内存可见性和指令重排
volatile 和 内存屏障

里面都提到了storeload这种JMM内存屏障,但是通过查看字节码发现volatile和普通变量生成的字节码没有任何区别。那么这些区别提现在哪里呢?我们就去看下它的汇编代码,发现指令多了lock修饰。

参考文章:
从汇编看Volatile的内存屏障
这篇文章也和我们的验证是一样的,用cpu指令lock来实现内存屏障。

查询IA32手册,它的作用是使得本CPU的Cache写入了内存,该写入动作也会引起别的CPU invalidate其Cache。所以通过这样一个空操作,可让前面volatile变量的修改对其他CPU立即可见。

所以,它的作用是
锁住主存:

  1. 任何读必须在写完成之后再执行
  2. 使其它线程这个值的栈缓存失效
  3. 类似于前面是storestore,后面是storeload

总结:个人理解,JMM语义定义了storeload等4个内存屏障,然后真正的实现是由cpu指令lock去模拟实现的,用来达到store load效果。

相关文章

网友评论

      本文标题:volatile

      本文链接:https://www.haomeiwen.com/subject/pnagfktx.html