重排序规则
  volatile和监视器锁

  其中普通读指getfield, getstatic, 非volatile数组的arrayload, 普通写指putfield, putstatic, 非volatile数组的arraystore。
  volatile读写分别是volatile字段的getfield, getstatic和putfield, putstatic。
  monitorenter是进入同步块或同步方法,monitorexist指退出同步块或同步方法。
  上述表格中的No指先后两个操作不允许重排序,如(普通写, volatile写)指非volatile字段的写入不能和之后任意的volatile字段的写入重排序。当没有No时,说明重排序是允许的,但是JVM需要保证小安全性-读取的值要么是默认值,要么是其他线程写入的(64位的double和long读写操作是个特例,当没有volatile修饰时,并不能保证读写是原子的,底层可能将其拆分为两个单独的操作)。
  final字段
  final字段有两个额外的特殊规则
  1、final字段的写入(在构造器中进行)以及final字段对象本身的引用的写入都不能和后续的(构造器外的)持有该final字段的对象的写入重排序。例如, 下面的语句是不能重排序的
  x.finalField = v; ...; sharedRef = x;
  2、final字段的第一次加载不能和持有这个final字段的对象的写入重排序,例如下面的语句是不允许重排序的
  x = sharedRef; ...; i = x.finalField
  内存屏障
  处理器都支持一定的内存屏障(memory barrier)或栅栏(fence)来控制重排序和数据在不同的处理器间的可见性。例如,CPU将数据写回时,会将store请求放入write buffer中等待flush到内存,可以通过插入barrier的方式防止这个store请求与其他的请求重排序、保证数据的可见性。
  内存屏障的分类
  几乎所有的处理器都支持一定粗粒度的barrier指令,通常叫做Fence(栅栏、围墙),能够保证在fence之前发起的load和store指令都能严格的和fence之后的load和store保持有序。通常按照用途会分为下面四种barrier
  LoadLoad Barriers
  Load1; LoadLoad; Load2;
  保证Load1的数据在Load2及之后的load前加载
  StoreStore Barriers
  Store1; StoreStore; Store2
  保证Store1的数据先于Store2及之后的数据 在其他处理器可见
  LoadStore Barriers
  Load1; LoadStore; Store2
  保证Load1的数据的加载在Store2和之后的数据flush前
  StoreLoad Barriers
  Store1; StoreLoad; Load2
  保证Store1的数据在其他处理器前可见(如flush到内存)先于Load2和之后的load的数据的加载。StoreLoad Barrier能够防止load读取到旧数据而不是近其他处理器写入的数据。
  几乎近代的所有的多处理器都需要StoreLoad,StoreLoad的开销通常是大的,并且StoreLoad具有其他三种屏障的效果,所以StoreLoad可以当做一个通用的(但是更高开销的)屏障。
  所以,利用上述的内存屏障,可以实现上面表格中的重排序规则

  为了支持final字段的规则,需要对final的写入增加barrier
  x.finalField = v; StoreStore; sharedRef = x;
  插入内存屏障
  基于上面的规则,可以在volatile字段、synchronized关键字的处理上增加屏障来满足内存模型的规则
  1、volatile store前插入StoreStore屏障
  2、所有final字段写入后但在构造器返回前插入StoreStore
  3、volatile store后插入StoreLoad屏障
  4、在volatile load前插入LoadLoad和LoadStore屏障
  5、monitor enter和volatile load规则一致,monitor exit 和volatile store规则一致。
  HappenBefore
  前面提到的各种内存屏障对应开发者来说还是比较复杂底层,因此JMM又可以使用一系列HappenBefore的偏序关系的规则方式来说明,要想保证执行操作B的线程看到操作A的结果(无论A和B是否在同一个线程中执行), 那么在A和B之间必须要满足HappenBefore关系,否则JVM可以对它们任意重排序。
  HappenBefore规则列表
  HappendBefore规则包括
  1、程序顺序规则: 如果程序中操作A在操作B之前,那么同一个线程中操作A将在操作B之前进行
  2、监视器锁规则: 在监视器锁上的锁操作必须在同一个监视器锁上的加锁操作之前执行
  3、volatile变量规则: volatile变量的写入操作必须在该变量的读操作之前执行
  4、线程启动规则: 在线程上对Thread.start的调用必须在该线程中执行任何操作之前执行
  5、线程结束规则: 线程中的任何操作都必须在其他线程检测到该线程已经结束之前执行
  6、中断规则: 当一个线程在另一个线程上调用interrupt时,必须在被中断线程检测到interrupt之前执行
  7、传递性: 如果操作A在操作B之前执行,并且操作B在操作C之前执行,那么操作A在操作C之前执行。
  其中显示锁与监视器锁有相同的内存语义,原子变量与volatile有相同的内存语义。锁的获取和释放、volatile变量的读取和写入操作满足全序关系,所以可以使用volatile的写入在后续的volatile的读取之前进行。
  可以利用上述HappenBefore的多个规则进行组合。
  例如线程A进入监视器锁后,在释放监视器锁之前的操作根据程序顺序规则HappenBefore于监视器释放操作,而监视器释放操作HappenBefore于后续的线程B的对相同监视器锁的获取操作,获取操作HappenBefore与线程B中的操作。