一、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屏障。
内存屏障,又称内存栅栏,是一组处理器指令,用于实现对内存操作的顺序限制。
内存屏障可以被分为以下几种类型
- LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
- StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
- LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。
- StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
参考文章:
Java并发:volatile内存可见性和指令重排
volatile 和 内存屏障
里面都提到了storeload这种JMM内存屏障,但是通过查看字节码发现volatile和普通变量生成的字节码没有任何区别。那么这些区别提现在哪里呢?我们就去看下它的汇编代码,发现指令多了lock修饰。
参考文章:
从汇编看Volatile的内存屏障
这篇文章也和我们的验证是一样的,用cpu指令lock来实现内存屏障。
查询IA32手册,它的作用是使得本CPU的Cache写入了内存,该写入动作也会引起别的CPU invalidate其Cache。所以通过这样一个空操作,可让前面volatile变量的修改对其他CPU立即可见。
所以,它的作用是
锁住主存:
- 任何读必须在写完成之后再执行
- 使其它线程这个值的栈缓存失效
- 类似于前面是storestore,后面是storeload
总结:个人理解,JMM语义定义了storeload等4个内存屏障,然后真正的实现是由cpu指令lock去模拟实现的,用来达到store load效果。
网友评论