原则(Happens"/>
Java 先行发生原则(Happens
读《深入理解Java虚拟机》第三版,周志明著。
读《Java并发编程的艺术》方腾飞、魏鹏、程晓明著。
读《Java并发编程实战》。
笔记。
先行发生原则可以判断数据是否存在竞争,线程是否安全。
如果两个操作之间缺乏 happens-before关系,那么 JVM 可以对它们任意地排序。
一、什么是 “ 先行发生 ” ?
先行发生是 Java 内存模型中定义的两项操作之间的偏序关系,比如说操作 A
先行发生于操作 B
,其实就是说在发生操作 B
之前,操作 A
产生的影响能被操作 B
观察到,“ 影响 ” 包括修改了内存中共享变量的值、发送了消息、调用了方法等。
《JSR-133: Java Memory Model and Thread Specification》对 happens-before 关系的定义如下:
- 如果一个操作 happens-before 另一个操作,那么第一个操作的执行结果将被第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
- 两个操作之间存在 happens-before 关系,并不意味着 Java 平台的具体实现必须要按照 happens-before 关系指定的顺序来执行。如果重排序之后的执行结果,与按 happens-before 关系来执行的结果一致,那么这种重排序并不非法(也就是说,JMM允许这种重排序)。
第一条是JMM对程序的承诺。从程序员的角度来说,可以这样理解 happens-before 关系:如果 A happens-before B,那么Java内存模型将向程序员保证——A操作的结果将对B可见,且A的执行顺序排在B之前。注意这只是Java内存模型向程序员做出的保证!
第二条是JMM对编译器和处理器重排序的约束原则。正如前面所言,JMM其实是在遵循一个基本原则:只要不改变程序的执行结果(指的是单线程程序和正确同步的多线程程序),编译器和处理器怎么优化都行。JMM这么做的原因是:程序员对于这两个操作是否真的被重排序并不关心,程序员关心的是程序执行时的语义不能被改变(即执行结果不能被改变)。
二、一些 “ 天然的 ” 先行发生关系
这些先行发生关系无须任何同步器协助就已经存在,可以在编码中直接使用。
- 程序次序规则(Program Order Rule):在一个线程内,按照
控制流顺序
,书写在签名的操作先行发生于书写在后面的操作。注意,这里说的是控制流顺序
而不是程序代码顺序
,因为要考虑分支、循环等结构。 - 管程锁定规则(Monitor Lock Rule):在一个
unlock 操作
先行发生于后面对同一个锁的lock 操作
。这里的 “ 后面 ” 是指时间上的先后。 - volatile 变量规则(Volatile Variable Rule):对一个
volatile 变量
的写操作
先行发生于后面对这个变量的读操作
,这里的 “ 后面 ” 同样是指时间上的先后。 - 线程启动规则(Thread Start Rule):Thread 对象的
start() 方法
先行发生于此线程的每个动作。 - 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的
终止检测
,我们可以通过Thread::join() 方法是否结束
、Thread::isAlive() 的放回值
等手段检测线程是否已经终止执行。 - 线程中断规则(Thread Interruption Rule):对线程
interrupt() 方法
的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread::interrupted() 方法
检测到是否有中断发生。 - 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的
finalize() 方法
的开始。 - 传递性(Transitivity):如果
操作 A
发行发生于操作 B
,操作 B
先行发生于操作 C
,那就可以得出操作 A
先行发生于操作 C
的结论。
三、换个角度理解 happens-before 规则
- 程序顺序规则:一个线程中的每个操作,happens-before 于该线程中的任意后续操作。
- 监视器规则:对一个锁的解锁,happens-before 于随后对这个锁的加锁。
- volatile 变量规则:对一个 volatile 域的写,happens-before 于任意后续对这个 volatile 域的读。
- 传递性:如果 A happens-before B,且 B happens-before C,那么 A happens-before C。
更多推荐
Java 先行发生原则(Happens
发布评论