happens-before原则

/ Jvm / 没有评论 / 323浏览

在Java中有一个很重要的原则,这个原则是判断线程是否存在数据竞争、线程是否安全的主要依据,这个原则就是happens-before原则。我们首先看一下到底什么是happens-before原则。简单来说happens-before原则就是JMM中定义的两项操作之间的偏向关系。假如我们有两个操作A和B。如果A操作先行发生于B操作,也就是A操作在B操作之前发生,那么在A操作在发生之后产生的影响B操作可以获取到,这里所说的影响主要包括修改共享变量、调用方法等。上述就是happens-before原则的定义,下面我们用一个简单的例子来说明一下happens-before原则到底有什么作用。

public class Test {
public static void main(String[] args) {
int i = 1;
int j = i;
i = 2;
}
}

我们假设上述代码中的3条语句分别由3个线程执行,并假如这3个线程分别是thread-1、thread-2、thread-3。如果我们指定thread-1要优先于thread-2执行那么我们就会得出结论thread-2在执行完成后,变量j的值一定是1。原因就是根据happens-before原则,thread-1先行发生于thread-2,所以thread-1执行后的值,thread-2是可以获取到的,并且我们假设thread-3没有执行。那么在thread-1执行后,变量i的值没有被其它线程修改过,所以thread-2在获取变量i时值为1。下面我们假设thread-1和thread-2还具有happens-before关系,也就是说thread-1在thread-2之前执行并且我们假设thread-2和thread-3并没有happens-before关系。也就是说thread-2和thread-3谁先执行并不是确定的。如果thread-3在thread-2之前执行的,那么thread-2中变量j的值是多少呢?答案可想而知,变量j的值是不确定的。因为我们并不确定thread-1和thread-3到底谁先执行。如果thread-3比thread-1先执行,那么因为thread-1和thread-2有happens-before关系,所以并不会对变量j的值产生影响,但如果thread-1比thread-3先执行,那么thread-2在获取变量i的值时,就是有可能获取到过期的数据了,这就造成的所谓的线程安全问题了。

在JMM中直接就具有happens-before原则,我们不需要进行任何同步,可以直接在编码中使用它们。如果代码中的操作没有happens-before原则的话,那么虚拟机在执行的时候,就有可能对它们进行随意的重排序。下面我们看一下在JMM中都有哪些操作是默认具有happens-before原则的。