7. 指令重排 与 happens-before
指令重排可以在 CPU 闲置(等待变量装载等)时,先执行其他指令,提高性能。分为:
- 编译器优化重排:编译器在不改变单线程程序语义的前提下,可以重新安排语句的执行顺序。
- 指令并行重排:现代处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性(即后一个执行的语句无需依赖前面执行的语句的结果),处理器可以改变语句对应的机器指令的执行顺序。
- 内存系统重排:由于处理器使用缓存和读写缓存冲区,这使得加载(load)和存储(store)操作看上去可能是在乱序执行,因为三级缓存的存在,导致内存与缓存的数据同步存在时间差。
指令重排在单线程时可以保证串行语义一致,但多线程时可能出现问题。
JMM 能保证指令重排后,不改变程序的执行结果:
- 单线程肯定能保证
- 多线程需要正确同步后,可以保证
7.1 happens-before 来保证指令的运行结果
happens-before 规范用来保证两个操作之间的执行顺序。这两个操作可以是一个线程内的,也可以是多线程的。
- 如果A happens-before B,则 A 在内存上做的操作对 B 都是可见的。
Java 中天然的 happens-before 关系:
- 程序顺序规则:一个线程中的每一个操作,happens-before于该线程中的任意后续操作。
- 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
- volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
- 传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
- start规则:如果线程A执行操作ThreadB.start()启动线程B,那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作
- join规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。