Java内存模型中 happens-before规则
- 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作课件,并且第一个操作的执行顺序排在第二个操作之前。
- 两个操作间存在happens-before关系,不意味着Java平台一定按照被该关系制定的顺序执行,如果重排序后不按照该顺序,但结果一致,那么这种重排序合法。
-
程序顺序规则:
- 一个线程中,按照程序执行的顺序,前面的操作happens-before于后面的操作。
- 也就是符合单线程的思维,程序在前面对变量的修改对后续操作可见。
-
监视器锁规则(管程锁):
- 一个锁的解锁 Happens-Before 于后续对这个锁的加锁
- 管程:通用的同步原语,在Java语言中是synchronized的实现
- synchronized中的加锁解锁操作是隐式的,前一个线程的解锁对后一个线程的加锁可见,这保证了对共享变量的可见性。
-
volatile变量规则:
- 对一个volatile域的写,Happens-Before于对该域的读。
-
传递性:A Happens-Before B,B Happens-Before C,则A Happens-Before C
-
结合例子来看:
class VolatileExample { int x = 0; volatile boolean v = false; public void writer() { x = 42; v = true; } public void reader() { if (v == true) { } } }
-
根据规则1:x=42 Happens-Before于 v=true
-
根据规则3:volatile 修饰的变量v=true的写 Happens-Before 于v=true的读
-
根据规则4:x=42 Happens-Before 于v=true的读,也就是我们读到的x为42
避免了多线程中CPU 缓存而导致可见性问题。
-
-
-
start()规则:父线程A中执行了子线程B的ThreadB.start()操作,则该操作 Happens-Before 于线程B中任何操作
Thread B = new Thread(()->{ }); // 此处对共享变量 a 修改对B可见 a = 77; // 主线程启动子线程 B.start();
-
join()规则:线程等待原则,如果在线程 A 中,调用线程 B 的 join() 并成功返回,那么线程 B 中的任意
操作 Happens-Before 于该 join() 操作的返回Thread B = new Thread(()->{ a = 66; }); B.start(); B.join() // 子线程所有对共享变量的修改在主线程调用 B.join() 之后皆可见,A中看到a=66
-
线程中断规则:对线程interrupt()方法的调用 Happens-Before 于被中断线程的代码检测到中断事件的发生,可以通过Thread.interrupted()方法检测到是否有中断发生。
-
对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。