voliatile

  1. 保证内存可见性
  2. 防止指令重排

    如何保证可见性

    每次读和写都从主存直接读写,而不使用缓存

    如何防止指令重排?

    在大多数情况下操作系统和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

不管如何重排序,单线程执行结果不会改变。
看上去就像有序执行一样。(虽然不是有序执行,但最终一致性)

参考资料

cpu缓存和volatile
cpu多级缓存
volatile和Cache一致性协议之MESI