指令重新排序和发生前关系
在《Java 并发实践》一书中,我们多次被告知,我们的程序的指令可以由编译器、运行时的 JVM 甚至处理器重新排序。因此,我们应该假设执行的程序不会以与我们在源代码中指定的顺序完全相同的顺序执行其指令。
但是,讨论 Java 内存模型的最后一章提供了发生在之前规则的列表,指示 JVM 保留了哪些指令排序。这些规则中的第一个是:
- “程序顺序规则。线程中的每个操作都发生在程序顺序后面的该线程中的每个操作之前。
我相信“程序顺序”指的是源代码。
我的问题是:假设有这个规则,我想知道什么指令实际上可以重新排序。
“操作”定义如下:
Java 内存模型是根据操作指定的,其中包括对变量的读取和写入、监视器的锁定和解锁以及线程的启动和连接。JMM 定义了在程序中的所有操作上称为之前发生的部分排序。为了保证执行操作 B 的线程可以看到操作 A 的结果(无论 A 和 B 是否出现在不同的线程中),A 和 B 之间必须存在 a 发生之前的关系。如果在两个操作之间排序之前没有发生,JVM 可以自由地随意对它们进行重新排序。
提到的其他顺序规则有:
- 监视锁定规则。监视器锁上的解锁发生在同一监视器锁上的每个后续锁定之前。
- 可变变量规则。对易失性字段的写入发生在每次后续读取同一字段之前。
- 线程启动规则。对线程上的 Thread.start 的调用发生在已启动线程中的每个操作之前。
- 线程终止规则。线程中的任何操作都会在任何其他线程检测到该线程已终止之前发生,无论是通过成功从 Thread.join 返回还是由 Thread.isAlive 返回 false。
- 中断规则。在另一个线程上调用中断的线程发生在中断线程检测到中断之前(通过引发 InterruptedException,或者调用中断或中断)。
- 终结器规则。对象的构造函数的结尾发生在该对象的终结器开始之前。
- 传递。如果 A 发生在 B 之前,B 发生在 C 之前,那么 A 发生在 C 之前。