Happens-Before 是一种可见性模型,也就是说,在多线程环境下。
原本因为指令重排序的存在会导致数据的可见性问题,也就是 A 线程修改某个共享变量 对 B 线程不可见。
因此,JMM 通过 Happens-Before 关系向开发人员提供跨越线程的内存可见性保证。
如果一个操作的执行结果对另外一个操作可见,那么这两个操作之间必然存在 Happens-Before 关系。
其次,Happens-Before 关系只是描述结果的可见性,并不表示指令执行的先后顺序, 也就是说 只要不对结果产生影响,仍然允许指令的重排序。
happens-before原则定义如下:
1. 如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,而且第一个操作的执行顺序排在第二个操作之前。
2. 两个操作之间存在happens-before关系,并不意味着一定要按照happens-before原则制定的顺序来执行。如果重排序之后的执行结果与按照happens-before关系来执行的结果一致,那么这种重排序并不非法。
JMM中的happens-before原则:
- 程序次序规则:一个线程中的每个操作, happens-before 这个线程中的任意后续操作;这个规则只对单线程有效,在多线程环境下无法保证正确性。
- 监视器锁规则:一个线程对于一个锁的释放锁操作, 一定 happens-before 于后续线程对这个锁的加锁操作;
- volatile变量规则:对一个 volatile 修饰的变量的写一定 happens-before 于任意后续对这个 volatile 变量的读操作;
- 传递规则:如果A happens-before B,且B happens-before C,那么A happens-before C。
- 线程启动规则:如果线程 A 执行操作 ThreadB.start(),那么线程 A 的 ThreadB.start()之前的操作 happens-before 线程 B 中的任意操作;
在这样一个场景中,t1 线程启动之前对于 x=10 的赋值操作,t1 线程启动以后读取 x 的值一定是 10
- 线程终结规则(join 规则):如果线程 A 执行操作 ThreadB.join()并成功返回,那么线程 B 中的任意操作 happens-before 于线程 A 从 ThreadB.join()操作成功的返回。;
(假定线程A在执行的过程中,通过制定ThreadB.join()等待线程B终止,那么线程B在终止之前对共享变量的修改在线程A等待返回后可见。)
- 线程中断规则:对线程interrupted()方法的调用先行于被中断线程的代码检测到中断事件的发生。
- 对象终结规则:一个对象的初始化完成(构造函数执行结束)先行于发生它的finalize()方法的开始。
happen-before原则是JMM中非常重要的原则,它是判断数据是否存在竞争、线程是否安全的主要依据,保证了多线程环境下的可见性。