3 防止指令重排
  除了前面内存可见性中讲到的volatile关键字可以保证变量修改的可见性之外,还有另一个重要的作用:在JDK1.5之后,可以使用volatile变量禁止指令重排序。
  解决方案:例子1中的inited和例子2中的instance以关键字volatile修饰之后,会阻止JVM对其相关代码进行指令重排,这样能够按照既定的顺序指执行。
  volatile关键字通过提供“内存屏障”的方式来防止指令被重排序,为了实现volatile的内存语义,编译器在生成字节码时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。
  大多数的处理器都支持内存屏障的指令。
  对于编译器来说,发现一个优布置来小化插入屏障的总数几乎不可能,为此,Java内存模型采取保守策略。下面是基于保守策略的JMM内存屏障插入策略:
  在每个volatile写操作的前面插入一个StoreStore屏障。
  在每个volatile写操作的后面插入一个StoreLoad屏障。
  在每个volatile读操作的后面插入一个LoadLoad屏障。
  在每个volatile读操作的后面插入一个LoadStore屏障。
  (三)总结
  volatile是轻量级同步机制
  相对于synchronized块的代码锁,volatile应该是提供了一个轻量级的针对共享变量的锁,当我们在多个线程间使用共享变量进行通信的时候需要考虑将共享变量用volatile来修饰。
  volatile是一种稍弱的同步机制,在访问volatile变量时不会执行加锁操作,也不会执行线程阻塞,因此volatilei变量是一种比synchronized关键字更轻量级的同步机制。
  volatile使用建议
  使用建议:在两个或者更多的线程需要访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,没必要使用volatile。
  由于使用volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。
  volatile和synchronized区别
  1、volatile不会进行加锁操作:
  volatile变量是一种稍弱的同步机制在访问volatile变量时不会执行加锁操作,因此也不会使执行线程阻塞,因此volatile变量是一种比synchronized关键字更轻量级的同步机制。
  2、volatile变量作用类似于同步变量读写操作:
  从内存可见性的角度看,写入volatile变量相当于退出同步代码块,而读取volatile变量相当于进入同步代码块。
  3、volatile不如synchronized安全:
  在代码中如果过度依赖volatile变量来控制状态的可见性,通常会比使用锁的代码更脆弱,也更难以理解。仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它。一般来说,用同步机制会更安全些。
  4、volatile无法同时保证内存可见性和原则性:
  加锁机制(即同步机制)既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性,原因是声明为volatile的简单变量如果当前值与该变量以前的值相关,那么volatile关键字不起作用,也是说如下的表达式都不是原子操作:“count++”、“count = count+1”。
  当且仅当满足以下所有条件时,才应该使用volatile变量:
  1、 对变量的写入操作不依赖变量的当前值,或者你能确保只有单个线程更新变量的值。
  2、该变量没有包含在具有其他变量的不变式中。
  总结:在需要同步的时候,第一选择应该是synchronized关键字,这是安全的方式,尝试其他任何方式都是有风险的。尤其在、jdK1.5之后,对synchronized同步机制做了很多优化,如:自适应的自旋锁、锁粗化、锁消除、轻量级锁等,使得它的性能明显有了很大的提升。