今天就让我们深入聊聊happens-before的那些事儿!什么是之前发生的?happens-before是指Java内存模型中两个操作之间的顺序关系。比如操作A先于操作B,也就是说操作A发生在操作B之前,操作A的影响可以通过操作B观察到。这里的“影响”包括:内存中共享变量的值,发送的消息,调用的方法等。举一个很简单的例子:下面的代码中,i=1在线程A中执行,j=i在线程B中执行。由于i=1操作在j=i之前执行,所以i=1操作的结果应该可以被线程B观察到。//Executei=1inthreadA;//在线程B中执行j=i;Java内存模型下有8条happens-before规则。如果线程之间的操作不能从以下规则推导出来,那么它们的操作就没有顺序保证,虚拟机或操作系统可以随意重新排序,这可能会导致并发安全问题。程序顺序规则(ProgramOrderRule):在一个线程中,按照程序代码顺序,写在前面的操作先于写在后面的操作发生。准确的说应该是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。监控锁规则(MonitorLockRule):一个解锁操作发生在同一个锁上的一个锁操作之前。这里必须强调的是同一个锁,“后”是指时间上的先后顺序。易失性变量规则(VolatileVariableRule):对易失性变量的写操作先发生,对变量的读操作后发生,这里的“后”也是指时间上的顺序。线程启动规则:Thread对象的start()方法先于线程的每一个动作。线程终止规则(ThreadTerminationRule):线程中的所有操作都发生在检测到该线程终止之前。我们可以通过Thread.join()方法的返回值Thread.join()方法结束检测到线程已经终止。终止执行。线程中断规则(ThreadInterruptionRule):当被中断线程的代码检测到中断事件发生时,首先调用线程interrupt()方法,可以通过Thread.interrupted()方法检测是否有中断事件发生发生了中断。FinalizerRule:一个对象的初始化完成(构造函数执行结束)发生在它的finalize()方法开始之前。传递性:如果操作A发生在操作B之前,操作B发生在操作C之前,那么可以得出操作A发生在操作C之前。极简实践案例Java语言不需要任何同步手段来保证前面的规则可以建立的是上面唯一的。这里有一个例子来说明如何使用这些规则来确定操作是否是顺序的和线程安全的。私有整数值=0;publicvoidsetValue(intvalue){this.value=value;}publicintgetValue(){returnvalue;}上面的代码是一组非常常见的getter/setter方法。假设线程A和B,线程A先调用setValue(1)(时间顺序),然??后线程B调用同一个对象的getValue(),那么线程B收到的返回值是多少?下面依次分析下happen-before原则中的每条规则:首先,由于这两个方法分别被线程A和线程B调用,不在同一个线程中,所以这里不适用程序顺序规则。那么,既然没有同步块,加锁和解锁操作自然不会发生,所以monitor加锁规则不适用。继续,因为值变量没有用volatile关键字修饰,所以volatile变量规则不适用。继续,下面的线程启动、终止、中断规则和对象终止规则与此无关。最后,传递性的最后一项也不在考虑范围内,因为没有适用的先发生规则规则。因此,即使我们知道线程A在运行时间上领先于线程B,我们仍然无法确定线程BgetValue()方法的返回结果。也就是说里面的操作不是线程安全的。那么如何解决这个问题呢?我们至少有两种更简单的解决方案可供选择:第一,要么将getter/setter方法定义为同步方法,这样就可以应用监视器锁定规则。第二种方式是将value定义为volatile变量。由于setter方法对value的修改不依赖于value的原值,满足volatile关键字的使用场景。这样就可以应用volatile变量的规则来实现首现关系。通过上面的案例我们知道,当一个操作在线上发生时,并不意味着这个操作会“先发生”。那么如果一个操作“先发生”,是否可以推导出这个操作在时间上一定是先发生的呢?事实上,它不能,因为可能会发生指令重排序。//下面的操作在同一个线程中执行intj=1;整数j=2;上面的代码是在同一个线程中执行的,按照程序执行顺序的规则,对inti=1进行操作;occursbeforeintj=2;,但是intj=2的代码可能会被处理器先执行,因为它们之间不依赖,不影响先发生原则的正确性。结合以上两个案例证明了一个结论:时间顺序和先发生的原则基本没有关系,所以我们在衡量并发安全问题的时候,不应该被时间顺序打乱,凡事一定要按照先发生的原则。允许。小结happens-before原则中有8条原则,简化了Java内存模型的规则,可以帮助程序员提高编程效率。时间顺序和先出现的原则基本没有关系。我们在衡量并发安全问题的时候,不应该被时间顺序打乱。凡事都要本着发生在先的原则。深刻理解happens-before原则
