voliatile
- 保证内存可见性
- 防止指令重排
如何保证可见性
每次读和写都从主存直接读写,而不使用缓存如何防止指令重排?
在大多数情况下操作系统和jvm为了提高执行效率,都会指令重排,在Java中,除了jvm规定的不能指令重排的特殊情况(hanppens-before原则)外,其他都可以指令重排。
硬件层面实现
一个是fence,一个是锁总线
fence: inter的mfence,lfence,sfence
软件层面
jvm层级:
- 8个hanppens-before原则
- 4个内存屏障
- as-if-serial
内存屏障
- storestore ss屏障之前的store操作必须先于屏障之后的操作
store1;
storestore;
store2; 该行指令不能重排到store1之前
- storeload
- loadstore
- loadload
volatile Object o;
当变量是对象引用时,内存屏障加在引用上,还是在对象内存区域? 加在对象所在内存区域,前后都加屏障。
hanppens-before 哪些指令不能重排?
在jvm中,如果一个操作对另一个操作可见,那么这两个操作必须遵守hanppens-before原则(jvm规定的指令重排原则):
- 程序次序规则
比如分支的if和else ,else if不能出现在if前面;
- 管程锁定规则
一个Unlock操作要先行发生在lock操作的后面
- volatile变量规则
对volatile变量的写操作要先行于对这个变量的读操作
- 线程启动规则 start 早于其他线程操作
- 线程终止规则
对线程的所有操作都必须先行于线程的终止检测,可以通过join和isAlive方法的返回值,等手段检测线程的中止。
- 线程中断规则
对线程interupt的调用先行于被中断线程检测到的中断事件的发生,可通过isInterupt方法检测线程是否中断。
- 对象终结规则 对象初始化先行于finalize
- 传递性 操作a先行于b,操作b先行于c,那么c为能先行于a
as-if-serial
不管如何重排序,单线程执行结果不会改变。
看上去就像有序执行一样。(虽然不是有序执行,但最终一致性)