本文主要是介绍happen-before与volatile、final,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
重排序与内存可见性问题
重排序有三种:(1)编译器重排序:编译器可以对没有先后依赖关系的语句重新排序。(2)CPU指令重排序:对没有依赖关系的指令重新排序。(3)CPU内存重排序:指令执行顺序和CPU缓存写入主内存的顺序不一致。
内存可见性问题主要是第三种重排序造成的。以下面例子为例:
线程1:X = 1 ; a = Y
线程2:Y = 1 ; b = X
理论上来看,最后可能出现三种情况:1、a = 0,b = 1。2、a = 1,b = 1。3、a = 1,b = 0。
但是,a和b都为0也是可能出现的,有可能线程1将X = 1写到了自己的CPU缓存中但还没写入到主内存中,那么线程2就无法将b设置为1,就可能出现a和b都为0。
happened-before的概念
如果A happened-before B,那么A的执行结果B必须可见。在JMM的happened-before描述的规范中,volatile变量不能重排序,对synchronized的解锁happpened-before后续对该锁的加锁。
happened-before的传递性
若A happened-before B,B happened-before C,那么A happened-before C。
final关键字的happened-before语义
对于final域的写happened-before与后续对该域的读。对于final域的读happened-before后续对该域的读。
volatile的happend-before
在CPU层面可以将JDK的内存屏障分为四种:
(1)LoadLoad: 禁止读和读的重排序。
(2)StoreStore:禁止写和写的重排序。
(3)LoadStore:禁止读和写的重排序。
(4)StroeLoad:禁止写和读的重排序。
JDK的Unsafe类提供三个内存屏障函数:
public native void loadFence();public native void storeFence();public native void fullFence();
其中,loadFence = LoadLoad + LoadStore,storeFence = StoreStore + LoadStore,fullFence = loadFence + storeFence + StoreLoad。
volatile实现原理:
(1)在volatile写操作前插入一个StoreStore屏障,使volatile写操作不会和之前的写操作重排序。
(2)在volatile写操作后插入一个StoreLoad屏障,使volatile写操作不会和后面的读操作重排序。
(3)在volatile读操作后面插入一个LoadLoad屏障和LoadStore屏障,使volatile读操作不会和后面的读操作、写操作重排序。
总结:happen-before四个基本规则
(1)单线程中的每个操作都happen-before后面的操作。
(2)对volatile的写,happen-before后面对该变量的读。
(3)对synchronized的解锁happen-before后续对该锁的加锁。
(4)对final变量的写,happen-before对final域对象的读,happen-before后续对final变量的读。
参考资料:《Java并发实现原理:JDK源码剖析》
这篇关于happen-before与volatile、final的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!