概述
本文大部分整理自《Java并发编程的艺术》,温故而知新,加深对基础的理解程度。
指令序列的重排序
我们在编写代码的时候,通常自上而下编写,那么希望执行的顺序,理论上也是逐步串行执行,但是为了提高性能,编译器和处理器常常会对指令做重排序。
1) 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
2) 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。
3) 内存系统的重排序。由于处理器使用缓存和读/写缓冲本文来源gaodai#ma#com搞@代~码^网+区,这使得加载和存储操作看上去可能是在乱序执行。
从Java源代码到最终实际执行的指令序列,会分别经历下面3种重排序:
happens-before语义
从JDK 5开始,Java使用新的内存模型,使用happens-before的概念来阐述操作之间的内存可见性。那到底什么是happens-before呢?
在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。
happens-before规则如下:
程序顺序规则: 对于单个线程中的每个操作,前继操作happens-before于该线程中的任意后续操作。
监视器锁规则: 对一个锁的解锁,happens-before于随后对这个锁的加锁。
volatile变量规则: 对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
传递性: 如果A happens-before B,且B happens-before C,那么A happens-before C。
注意:
两个操作之间具有happens-before关系,并不意味着前一个操作必须要在后一个操作之前执行,happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。
happens-before与JMM的关系如图所示:
如图所示,一个happens-before规则对应于一个或多个编译器和处理器重排序规则。
重排序
重排序指的是:编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。
如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖性。数据依赖分为下列3种类型:
上面情况,只要重排序两个操作的执行顺序,程序的执行结果就会被改变。而编译器和处理器可能会对操作做重排序,但是编译器和处理器在重排序时,会遵守数据依赖性,编译器和处理器不会改变存在数据依赖关系的两个操作的执行顺序。
注意:
这里所说的数据依赖性仅针对单个处理器中执行的指令序列和单个线程中执行的操作,不同处理器之间和不同线程之间的数据依赖性不被编译器和处理器考虑。