JVM 先行发生原则(happens-before)
2019-01-14 本文已影响2人
SlowGO
1. 什么是先行发生原则(happens-before)
先行发生是Java内存模型中定义的两项操作之间的偏序关系,如果说操作A先行发生于操作B,就是说A产生的影响能被B观察到,”影响“包括修改了内存中的共享变量值、发送了消息、调用了方法等。
例如:
// 线程A中执行
i = 1;
// 线程B中执行
j = i;
// 线程C中执行
i = 2;
如果说线程A是先行发生于线程B的,那么可以确定在线程B执行之后 j=1
,因为根据先行发生原则,A操作 i = 1
的结果可以被B观察到,并且线程C还没有执行。
那么如果线程C是在A与B之间,j
的值是多少呢?答案是不确定。
2. 自动实现先行发生的规则
以下是Java内存模型中天然的先行发生规则,对于不在此列的关系,就没有顺序性保障,虚拟机可以随意的进行重排:
- 程序次序规则:代码执行顺序符合流程控制顺序。
- 管程锁定规则:unlock 操作先行发生于后面对同一个锁的 lock 操作。
- volatile 变量规则:对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。
- 线程启动规则:线程对象 start() 方法先行发生于此线程的每一个动作。
- 线程终止规则:线程中所有操作先行发生于对此线程的终止检测。
- 线程中断规则:对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发送:可以通过 Thread.interrupted() 方法检测到是否有中断发生。
- 对象终结规则:一个对象的初始化完成先行发生于它的 finalize() 方法的开始。
- 传递性:如果操作A先行发生于操作B,B先行发生于C,那么A先行发生于C。
3. 示例
private int value = 0;
public void setValue(int value) {
this.value = value;
}
public int getValue(){
return value;
}
假设有2个线程 A 和 B,A 先调用了 setValue(1)
,然后 B 调用 get 方法,那么 B 的返回值是什么?
我们对照一下上面的那些原则:
- 2个方法分别在2个线程中调用,不在一个线程中,”程序次序规则“不适用;
- 没有同步块,不会发生 lock 和 unlock 操作,”管程锁定规则“不适用;
- value 没有使用 volidate 关键字,”volatile 变量规则“不适用;
- 其他的线程、对象的启动终结之类的规则和此代码没有关系,都不适用;
所以,B 的返回值无法确定,就是说线程不安全。