JVM 先行发生原则(happens-before)

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 的返回值无法确定,就是说线程不安全。

你可能感兴趣的:(JVM 先行发生原则(happens-before))