1. happens-before
1.1 在 JSR-133 中的定义
- 如果一个操作 happens-before 另一个操作,那么第一个操作的执行结果将对第二个操作可见
- 两个操作之间存在 happens-before 关系,并不意味着 Java 平台的具体实现必须要按照 happens-before 关系指定的顺序来执行。如果重排序之后的执行结果与按 happens-before 关系执行的结果一致,这种重排序是允许的
happens-before保证正确同步的多线程程序的执行结果不被改变
1.2 在JSR-133 中定义的具体规则
- 程序顺序规则:一个线程中的每个操作,happens-before 于该线程中的任意后续操作
- 监视器锁规则:对一个锁的解锁,happens-before 于随后对这个锁的加锁
- volitile变量规则:对一个 volitile 变量的写 happens-before 于任意后续对这个 volitile 变量的读
- 传递性:如果 A happens-before B,B happens-before C,A happens-before C
- start()规则:如果线程 A 执行操作 ThreadB.start(),那么 A 线程的 ThreadB.start() happens-before 于线程B中的任意操作
- join()操作:如果线程 A 执行操作 ThreadB.join()并成功返回,那么线程 B 中的任意操作 happens-before 于线程 A 从 ThreadB.join()操作成功返回
2. as-if-serial 语义
不管怎么重排序,单线程程序的执行结果不能被改变。
为了遵守 as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序。
3. 重排序
(1)编译器重排序:编译器在不改变单线程程序执行结果的前提下,可以重新安排语句的执行顺序
(2)指令集并行重排序:现代处理器采用了指令集并行技术来重叠执行多条指令。如果不存在数据依赖性,处理器可以改变语句对应机器指令的执行顺序
(3)内存系统重排序:处理器存在高速缓存(L1、L2、L3)、读/写缓冲区(Store Buffer/Invalid Queue),加载和存储操作看上去可能是在乱序执行
(1)属于编译器重排序,(2)、(3) 属于处理器重排序
- 对于编译器重排序,JMM 会禁止特定类型的编译器重排序
- 对于处理器重排序,JMM要求 java 编译器在生成指令序列时插入特定类型的内存屏障指令,通过内存屏障指令来禁止特定类型的处理器重排序
4. 内存屏障
- LoadLoad: Load1; LoadLoad; Load2
- LoadStore: Load1; LoadStore; Store1
- StoreLoad: Store; StoreLoad; Load
- StoreStore: Store; StoreStore; Store