重排序是什么
计算机在执行程序的时候,为提高性能,编译器和处理器会对指令重排。 指令重排可以保证串行语义一致性,但没有保证多线程间语义一致性。
指令重排多的三种情况
- 不改变单线程语义的情况下,重新安排语句的执行顺序
- 指令级并行技术,不存在数据依赖性处理器可以改变语句对应机器指令的执行顺序
- 内存重排处理器使用缓存和读写缓冲区,导致内存与缓存存在数据同步时间差
JMM
数据竞争与顺序一致性
程序未正确同步的时候,就可能存在数据竞争。 程序的执行结果和该程序在顺序一致性模型中执行的结果相同。
顺序一致性模型(理想化理论参考模型)
特性:
- 一个线程的所有操作必须按照程序的顺序执行
- 每个操作必须原子性,且立刻对所有线程可见
注意:
JMM中没有这样的保证当前线程把写过的数据缓存在本地内存中,在没有刷新到主内存之前,这个写操作仅仅只对当前线程可见。这种情况下当前线程与其他线程看到的执行顺序不一样。
JMM同步程序顺序一致性效果
在顺序一致性模型中,所有操作完全按照程序的顺序串行执行。但是jmm临界区(同步块)内可以发生重排序。
JMM未同步顺序一致性的效果
- 因为重排序,但是JMM保证单线程下的重排序不影响执行结果
- JMM不保证对64位的long型和double型变量的写操作具有原子性
- JMM不保证所有线程能看到一致的操作执行顺序
happens-before
对编译器来说,只要不改变程序的执行结果(单线程程序和正确同步了的多线程程序),编译器和处理器怎么优化都行。 JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证。
关系定义如下
-
- 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
-
- 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,那么JMM也允许这样的重排序。
天然的happens-before关系
- 程序顺序规则:一个线程内,每个操作happens-before后续操作
- 监视器锁规则:对一个锁的解锁,happens-before对这个锁的加锁
- volitile:对这个的写happens-before任意后续对这个的读
- 传递性:a happens-brefore b b happens-before c 则a happens-before c
- start规则:如果线程a操作线程b的start ,a的start操作happens-before b 的任意操作。
- join规则:如果a操作执行b join 那么线程b中的任意操作happens-before于线程a从bjoin成功返回。