0°

happens-before原则

在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原则的。

  • 程序次序规则:在一个线程内,按照程序代码顺序,写在前面的代码先行发生于写在后面的代码。(暂时不考虑分支判断、循环等)
  • 管程锁定规则:一个unlock操作先行发生于后面对同一个锁的lock操作。
  • volatile变量规则:对一个volatile变量的写操作先行发生于后面对这个变量的读操作。
  • 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作。
  • 线程终止规则:线程中的所有操作都先行发生于对此线程的终止检测。
  • 对象终结规则:一个对象的初始化先行发生于它的finalize()方法。
  • 传递性:如果操作A先行发生于操作B,操作B先行发生于操作C,那操作也A先行发生于操作C。
未分类
0 条回复 A 作者 M 管理员
    所有的伟大,都源于一个勇敢的开始!
欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论