重排序是编译器和处理器为了优化性能而对指令执行的顺序进行重排序。大多数现代处理器都会采用将指令乱序执行的方法,在条件允许的情况下,直接运行当前有能力立即执行的后续指令,避开获取下一条指令所需数据时造成的等待。通过乱序执行的技术,处理器可以大大提高执行效率。
也就是说程序的执行,并不是严格按照程序语句编写的顺序执行,在运行期间可能是被打乱的。
程序在执行的时候发成重排序,那Java是如何保证程序正常执行的呢? 原因就是Java遵循as-if-serial语义,即单线程执行程序时,即使发生重排序,程序的执行结果不能被改变。 as-if-serial保证了Java程序在单线程运行的情况下,结果的正常,让我们看起来像是顺序执行的样子。
为了使Java程序在各个平台执行正常,Java内存模型中规定了happens-before规则。happens-before的前后两个操作不会被重排序且后者对前者内存可见。
volatile有两个语义: 1. 多线程对于域的可见性 2. 禁止指令重排序
第二个禁止对volatile的指令重排序在上面已经说明,现在说明JVM使如何保证volatile的可见性。
Java在处理volatile读写操作时会加入相应的内存屏障,来保证一个读写的循序。
内存屏障(Memory Barrier,或有时叫做内存栅栏,Memory Fence)是一种CPU指令,用于控制特定条件下的重排序和内存可见性问题。Java编译器也会根据内存屏障的规则禁止重排序。
LoadLoad屏障:对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。 StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。 LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。 StoreLoad屏障:对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。它的开销是四种屏障中最大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功能。
