Happens-Before详解

张开发
2026/4/10 15:15:24 15 分钟阅读

分享文章

Happens-Before详解
Happens-Before是指多线程共享一个变量时前面线程对变量的修改对后面的线程可见。Happens-Before规则就是要保证线程之间的共享变量可见性。JVM的编译器在对代码进行编译时需要遵循Happens-Before原则确保编译器优化后程序的执行结果也遵守Happens-Before原则。JMM定义了一套自己的规则Happens-Before先行发生规则并且确保只要两个Java语句之间必须存在Happens-Before关系JMM尽量确保这两个Java语句之间的内存可见性和指令有序性。1.程序的顺序性规则顺序性规则的具体内容一个线程内按照代码顺序书写在前面的操作先行发生(Happens-Before)于书写在后面的操作。前面操作产生的结果必须对后面操作可见。换句话说单个线程中的代码顺序无论怎么重排序对于结果来说是不变的。一段程序的执行在单个线程中看起来是有序的。程序次序规则看起来是按顺序执行的因为虚拟机可能会对程序指令进行重排序。虽然进行了重排序但是最终执行的结果与程序顺序执行的结果是一致的。它只会对不存在数据依赖行的指令进行重排序。该规则就是前面介绍的As-if-Serial规则仅仅用来保证程序在单线程执行结果的正确性但是无法保证程序在多线程执行结果的正确性。2.volatile变量规则这条规则用于对volatile关键字行为进行约束。一个volatile变量的写操作对后续该变量的读操作遵循Happens-Before原则。即一个线程对volatile变量的修改对另外一个线程实时可见。对volatile变量的读写之前的操作不能重排序到之后之后的操作也不能重排序到之前。3.传递原则这条规则是简单的逻辑推导原则。如果A操作的结果对B操作可见且B操作的结果对C操作可见那么A操作的结果一定对C可见。传递性规则的具体内容如果A操作先行发生于B操作且B操作先行发生于C操作那么A操作先行发生于C操作。4.锁的规则监视锁规则的具体内容对一个锁的unlock操作先行发生于后面对同一个锁的lock操作即无论在单线程还是多线程中同一个锁如果处于被锁定状态那么必须先对锁进行释放操作后面才能继续执行lock操作。临界区内的代码可以重排序临界区之间的代码不能重排序。5.start规则这个规则是用来确保在调用start方法启动线程之前父线程已经对变量做出的修改在子线程中是实时可见的。具体来说如果线程A执行B.start()启动线程B那么线程A的B.start()操作先行发生于线程B中的任意操作。线程B能看到线程A在启动操作前的任何操作。6.join规则这条原则是针对线程间同步等待的当一个线程A等待另一个线程B运行结束当线程B运行结束后线程A要能看到线程B对共享变量的修改。也就是如果线程A执行threadB.join()操作并成功返回那么线程B中的任意操作先行发生于线程A的ThreadB.join()操作。那么线程B中的任意操作先行发生于线程A所执行的ThreadB.join()操作。join()规则和start()规则刚好相反线程A等待子线程B完成后当前线程B的赋值操作线程A都能够看到。7.final字段的特殊性final修饰的字段无论在单线程还是多线程里都是线程安全的不可变对象。当一个对象的构造函数执行完成时可以认为该构造函数进行了完全的初始化。只有在对象完全初始化后才能被对象的引用线程看到。在构造函数为final字段赋值和在另一个线程中读取对象的值这两个动作之间有一个Happens-Before的机制。在构造函数执行完成之前所有对于final字段的写操作都会被冻结只有构造函数完成之后才能进行字段读取。

更多文章