概述
本文参考《Java并发编程的艺术》一书,温故而知新,加深对基础的理解。
一、指令序列的重排序
我们在编写代码的时候,通常自上而下编写,那么希望执行的顺序,理论上也是逐步串行执行,但是为了提高性能,编译器和处理器常常会对指令做重排序。从Java源码到最终实际的指令,需要经过三个阶段的重排序:
1) 编译器优化的重排序。编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。 2) 指令级并行的重排序。现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序。 3) 内存系统的重排序。由于处理器使用缓存和读/写缓冲区,这使得加载和存储操作看上去可能是在乱序执行。
JMM属于语言级别的内存模型,他确保在不同的编译器和不同的处理器平台上,通过禁止特定类型的编译器重排序和处理器重排序,为程序员提供一致的内存可见性保证。
二、as-if-serial语义
as-if-serial语义的含义是:无论如何重排序,单线程程序的执行结果不能被改变。编译器和处理器都必须遵循as-if-serial语义,编译器和处理器为了遵循这一易于,他们就不会对存在数据依赖关系的操作进行重排序。具体来说就是如下表的操作就不会重排序:
as-if-serial语义把单线程程序保护了起来,遵守as-if-serial语义的编译器、runtime和处理器共同为编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。
三、happens-before规则
happens-before是JMM的核心概念,通过happens-before规则JMM可以实现:
- 一方面,为程序员提供了足够强的内存可见性保证;
- 另一方面,对编译器和处理器的限制要尽可能的宽松;

1、happens-before规则
JSR-133规范中定义了如下happens-before规则:
(1)程序顺序规则:一个线程中的每个操作,happens-brfore于该线程中的任意后序操作。
(2)监视器锁规则:线程对解锁obj之前的对变量的写,对接下来对obj加锁的线程对变量的读可见。
static int x=0;
static final Object obj=new Object();
new Thread(()->{
synchronized(obj){
x=1;
}
},"t1").start();
new Thread(()->{
synchronized(obj){
//t1对x的修改对于t2是可见的,因此将会打印2
System.out.println(x++);
}
},"t2").start();
(3)volatile变量规则:一个线程对volatile的写,对接下其他变量对该变量的读可见。
volatile static int x=0;
new Thread(()->{
synchronized(obj){
x=10;
}
},"t1").start();
new Thread(()->{
synchronized(obj){
//t1对x的修改对于t2是可见的,因此将会打印10
System.out.println(x);
}
},"t2").start();
(4)传递性:如果A happens-before B,且 B happens-before C,那么 A happens-before C。
(5)start()规则:线程start()之前对变量的写,对该变量开始后对该变量可见。
static int x;
x=10;
new Thread(()->{
synchronized(obj){
//t1启动之前对x的写对它是可见的,因此将会打印10
System.out.println(x);
}
},"t1").start();
(6)join()规则:线程结束前对变量的写,对其他线程得知他结束后的读可见。(比如线程调用isAlive()、join())
static int x;
Thread t1=new Thread(()->{
synchronized(obj){
x=10;
}
},"t1");
t1.start();
t1.join();
//t1启动之前对x的写对它是可见的,因此将会打印10
System.out.println(x);
总的来说:happens-before和as-if-serial语义是一回事
- as-if-serial语义保证单线程内程序的执行结果不被重排序改变,hapeens-before关系保证正确同步的多线程程序的执行结果不被重排序改变。
- as-if-serial语义给编写单线程程序的程序员创建了一个幻觉:单线程程序是按程序的顺序来执行的。happens-before规则给编写正确同步的多线程程寻的程序员一个幻觉:正确同步的多线程程序是按happens-before指定的顺序来执行的。