volatile关键字
# 1 volatile关键字的理解?
被volatile修饰的共享变量具有2个特性。
1.保证了不同线程之间操作该共享变量的内存可见性。
2.禁止指令重排序
# 2 什么是内存可见性?
# 2.1 Java内存模型
关于内存可见性的话,要先提一下Java内存模型(JMM),Java虚拟机里面定义的一种抽象模型。
根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。
每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。

但是JMM这个主内存和工作内存跟JVM中的堆、栈内存实际上是没什么关系的。
# 2.2 jvm和jmm之间的关系
jmm中的主内存、工作内存与jvm中的Java堆、栈、方法区等并不是同一个层次的内存划分,这两者基本上是没有关系的。
如果一定要勉强对应起来,jmm的主内存可以对应jvm堆中对象实例部分,jmm工作内存可以对应jvm虚拟机栈中的部分区域。
从更低层次上说,主内存就直接对应于物理硬件的内存,
而为了获取更好的运行速度,虚拟机可能会让工作内存优先存储于寄存器和高速缓存中,因为程序运行时主要访问读写的是工作内存。
# 2.3 内存可见性
volatile保证了变量的可见性主要是因为:
1)线程更新volatile变量的时候,先去更新工作内存中这个变量的副本,然后再将改变后副本的值从工作内存刷新到主内存。
2)线程读取volatile变量的时候,先去主内存中读取最新值到工作内存,然后再从工作内存中读取。
# 3 什么是指令重排序?
# 3.1 指令重排序
重排序是指编译器和处理为了优化程序性能而对指令序列进行重新排序的一种手段。
如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重新排序。

# 3.2 happens-before(先行发生原则)
Java内存模型中会默认保证一定的有序性,就是happens-before规则,指令重排序需要遵循这个规则。
如果2个操作的执行顺序无法重happens-before规则中推导出来,就不能保证他们的有序性,jvm就可能对他们进行重排序。
happens-before主要有以下几条规则:
1.程序次序规则: 一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。(保证单线程的执行结果是正确的,对于无关紧要的重排序是允许的。)
2.锁定规则: 一个unLock操作先行发生于后面对同一个锁的lock操作。
3.volatile变量规则: 对一个变量的写操作先行发生与后面对这个变量的读操作。
4.传递规则: 如果操作A先行发生于操作B,而操作B又先发生于操作C,则可以得出操作A先行发生于操作C。
前四条规则是比较重要的,后四条是比较显而易见的。
1.线程启动规则: Thread对象的start()方法先行发生于此线程的每一个动作
2.线程中断规则: 对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生。
3.线程终结规则: 线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行。
4.对象终结规则: 一个对象的初始化完成先行发生于他的finalize()方法的开始。