youyichannel

志于道,据于德,依于仁,游于艺!

0%

Java内存模型 —— JMM_04

happens-before原则

JMM核心概念。

JDK1.5开始,Java使用新的JSR-133内存模型,JSR-133使用happens-before的概念来阐述操作之间的内存可见性。在JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作(可以是在一个线程之内,也可以是在不同线程之间)之间必须要存在happens-before关系。

和程序员密切相关的happens-before规则:

  1. 程序顺序规则:一个线程中的每个操作,happens-before于该线程后的任意后续操作
  2. 监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁
  3. volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读
  4. 传递性:若A happens-before B,且B happens-before C,则A happens-before C
  5. start()规则:如果线程A执行操作ThreadB.start(),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作
  6. join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回

两个操作之间具有happens-before关系,并不意味前一个操作必须要在后一个操作之前执行。happens-before仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前。

《JSR-133: Java Memory Model and Thread Specification》 对happens-before关系的定义:

  1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前
  2. 两个操作之间存在happens-before关系,并不意味着Java平台的具体实现必须要按照happens-before关系指定的顺序来执行。如果重排序之后的执行结果,与按happens-before关系来执行的结果一致,这种重排序并不非法,JMM允许这种重排序。

【🌰栗子】

double pi = 3.14;			// A
double r = 1.0; // B
double area = pi * r * r; // C

上述操作存在3个happens-before关系:

  1. A happens-before B
  2. B happens-before C
  3. A happens-before C

这三个happens-before关系中,2和3是必需的,1是不必要的,A、B可以执行重排序(并行执行)。

as-if-serial语义

不管如何重排序,单线程程序的执行结果不能改变。编译器、Runtime和处理器都必须遵守as-if-serial语义。

为了遵循as-if-serial语义,编译器和处理器不会对存在数据依赖关系的操作做重排序,因为这种重排序会改变执行结果。但是,如果操作之间不存在数据依赖关系,这些操作就可能被编译器和处理器重排序。

总结

happens-before关系本质上和as-if-serial语义是一样的。

  1. as-if-serial语义保证单线程内程序的执行结果不被改变,happens-before关系保证正确同步的多线程程序的执行结果不被改变
  2. as-if-serial语义给编写单线程程序的程序员创造了一个幻境:单线程程序是按照程序的顺序来执行的
  3. happens-before关系给编写正确同步的多线程程序的程序员创造了一个幻境:正确同步的多线程程序是按照happens-before指定的顺序来执行的
  4. as-if-serial语义和happens-before规则这么做的目的,都是为了在不改变程序执行结果的前提下,尽可能地提高程序执行的并行度

资料:https://www.bilibili.com/video/BV1VN411T7xX/ 强推 河北王校长