Java 知识 -- happens-before 原则
本文最后更新于:5 天前
JSR-133使用 happens-before 的概念来阐述操作之间的内存可见性。
在 JMM 中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在 happens-before 关系。这里提到的两个操作既可以是在一个线程之内,也可以是在不同线程之间。
注意:
两个操作之间具有 happens-before 关系,并不意味着前一个操作必须要在后一个操作之前执行! happens-before 仅仅要求前一个操作(执行的结果)对后一个操作可见,且前一个操作按顺序排在第二个操作之前(the first is visible to and ordered before the second)。
⚡happens-before 部分规则
-
程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作。
主要含义是:在一个线程内不管指令怎么重排序,程序运行的结果都不会发生改变。和as-if-serial 比较像。
-
监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁。
主要含义是:同一个锁的解锁一定发生在加锁之后
-
管程锁定规则:一个线程获取到锁后,它能看到前一个获取到锁的线程所有的操作结果。
主要含义是:无论是在单线程环境还是多线程环境,对于同一个锁来说,一个线程对这个锁解锁之后,另一个线程获取了这个锁都能看到前一个线程的操作结果!(管程是一种通用的同步原语,synchronized就是管程的实现)
-
volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读。
主要含义是:如果一个线程先去写一个volatile变量,然后另一个线程又去读这个变量,那么这个写操作的结果一定对读的这个线程可见。
-
传递性:如果A happens-before B,且B happens-before C,那么A happens-before C。
-
start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作。
主要含义是:线程A在启动子线程B之前对共享变量的修改结果对线程B可见。
-
join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回。
主要含义是:如果在线程A执行过程中调用了线程B的join方法,那么当B执行完成后,在线程B中所有操作结果对线程A可见。
-
线程中断规则:对线程interrupt方法的调用happens-before于被中断线程的代码检测到中断事件的发生。
主要含义是:响应中断一定发生在发起中断之后。
-
对象终结规则:就是一个对象的初始化的完成,也就是构造函数执行的结束一定 happens-before它的finalize()方法。
一个happens-before规则对应于一个或多个编译器和处理器重排序规则。
⚡as-if-serial 和 happens-before 原则
as-if-serial 和 happens-before 的主要作用都是:在保证不改变程序运行结果的前提下,允许部分指令的重排序,最大限度的提升程序执行的效率。
他们的区别是:前者只保证单线程下的有序性,而后者则是保证多线程下的程序执行有序性和可见性(多线程才有可见性一说,而且保证的前提是遵循了这些原则)
⚡Java 中的有序性
-
如果在本地线程内观察,所有操作都是有序的(“线程内表现为串行”(Within-Thread As-If-Serial Semantics));
-
如果在一个线程中观察另一个线程,所有操作都是无序的(“指令重排序”现象和“线程工作内存与主内存同步延迟”现象)。
Java语言提供了volatile和synchronized两个关键字来保证线程之间操作的有序性:
-
volatile关键字本身就包含了禁止指令重排序的语义
-
synchronized则是由“一个变量在同一个时刻只允许一条线程对其进行lock操作”这条规则获得的,这个规则决定了持有同一个锁的两个同步块只能串行地进入
本博客所有文章除特别声明外,均采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 。转载请注明出处!