重排序和happens-before

98 阅读3分钟

重排序是什么

计算机在执行程序的时候,为提高性能,编译器和处理器会对指令重排。 指令重排可以保证串行语义一致性,但没有保证多线程间语义一致性。

指令重排多的三种情况

  • 不改变单线程语义的情况下,重新安排语句的执行顺序
  • 指令级并行技术,不存在数据依赖性处理器可以改变语句对应机器指令的执行顺序
  • 内存重排处理器使用缓存和读写缓冲区,导致内存与缓存存在数据同步时间差

JMM

数据竞争与顺序一致性

程序未正确同步的时候,就可能存在数据竞争。 程序的执行结果和该程序在顺序一致性模型中执行的结果相同。

顺序一致性模型(理想化理论参考模型)

特性:

  • 一个线程的所有操作必须按照程序的顺序执行
  • 每个操作必须原子性,且立刻对所有线程可见

注意:

JMM中没有这样的保证当前线程把写过的数据缓存在本地内存中,在没有刷新到主内存之前,这个写操作仅仅只对当前线程可见。这种情况下当前线程与其他线程看到的执行顺序不一样。

JMM同步程序顺序一致性效果

在顺序一致性模型中,所有操作完全按照程序的顺序串行执行。但是jmm临界区(同步块)内可以发生重排序。

JMM未同步顺序一致性的效果

  • 因为重排序,但是JMM保证单线程下的重排序不影响执行结果
  • JMM不保证对64位的long型和double型变量的写操作具有原子性
  • JMM不保证所有线程能看到一致的操作执行顺序

happens-before

对编译器来说,只要不改变程序的执行结果(单线程程序和正确同步了的多线程程序),编译器和处理器怎么优化都行。 JMM可以通过happens-before关系向程序员提供跨线程的内存可见性保证。

关系定义如下

    1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
    1. 两个操作之间存在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成功返回。