当前位置: 首页 > 科技观察

面试官:你了解过指令重排序吗?什么是Happens-Before

时间:2023-03-22 11:32:05 科技观察

重新排序?首先,什么是重排序?计算机在执行过程中,为了提高性能,会对编译器和编译器的指令进行重排序。为什么这样可以提高性能呢?我们知道计算机在执行的时候,是一条一条的执行指令。不同的指令可能运行在不同的硬件上,执行过程中可能会出现中断。例如,两条指令a和b的操作是不同的。如果加载a的时候有停顿,b就不会加载,但实际上它们是相辅相成的。我也可以先加载b再加载a,这样指令重排就是为了减少停顿。一种大大提高效率的方法。指令重排的方式指令重排一般分为以下三种:编译器优化重排语句的执行顺序。指令并行重排采用指令级并行技术,并行执行多条指令。如果指令前没有数据依赖,处理器可以改变相应机器指令的执行顺序。内存系统重新排序由于处理使用缓存和读写缓冲区,因此它们是乱序的。指令重排可以保证串行语义一致,但没有义务保证多线程之间语义一致**。所以在多线程下,指令重排序可能会导致一些问题。顺序一致性模型顺序一致性模型是一个“理论参考模型”。内存模型的设计参考了顺序一致性内存模型。数据竞争我们知道,在多线程的情况下,同时读写一个变量会导致结果的不确定性,也就是说存在数据竞争。反之,如果线程是同步的,则不存在数据竞争。JMM在同步多线程的情况下,程序执行可以保证顺序的一致性。同步包括使用volatile、final、synchronized等关键字来实现“多线程下的同步”。这里的前提是正确使用它们。如果使用不当,我们无法保证顺序一致性模型是什么。上一节我们讲了Java内存模型,提到了内存可见性的概念。顺序一致性模型的最终目标是保证内存的可见性。它有两个主要特点:一个线程中的所有操作都必须按照程序的顺序(代码顺序)执行。无论线程是否同步,所有线程都保持单一的执行顺序,并且是可见的和原子的。JMM中的同步顺序一致性在JMM中,临界区(synchronized方法或synchronized块)中的代码可以重新排列,但对于其他线程是察觉不到的,在不影响最终结果的情况下提高了执行效率。JMM中的非同步顺序一致性JMM不保证非同步程序的执行结果与顺序一致性程序的执行结果一致。JMM不保证单个线程内的操作将按程序顺序执行(因为指令重新排序)。JMM不保证所有线程将看到执行操作的一致顺序(因为不保证操作立即可见)。JMM不保证对64位long和double变量的写操作的原子性。Whatishappens-beforeJMM提供了“happens-beforerule”(JSR-133规范),开发者可以按照这个规范编写程序,可以保证程序在JMM中有很强的内存可见性。JMM使用happens-before的概念来自定义两个操作之间的执行顺序。这两个操作可以在一个线程内,也可以在不同线程之间。因此,JMM可以通过happens-before关系为程序员提供跨线程的内存可见性保证。happens-before关系定义如下:如果一个操作happens-before另一个操作,那么第一个操作的执行结果将对第二个操作可见,并且第一个操作的执行顺序在第二个操作之前。“两个操作之间存在happens-before关系并不意味着Java平台的具体实现必须按照happens-before关系规定的顺序执行,如果重新排序后的执行结果按照happens-beforerelationship如果操作A的结果是一致的,那么JMM也允许这样的重新排序。简而言之,“如果操作Ahappens-before操作B,那么操作A对内存的操作对于操作B是可见的,无论它们是否在同一个线程中。。”在Java中,有以下天然的happens-before关系:程序顺序规则:线程中的每一个操作,happens-before线程中的任何后续操作。锁。易失性变量规则:对易失性字段的写入发生在任何后续读取易失性字段之前。传递性:如果A发生在B之前,并且B发生在C之前,则A发生在C之前。启动规则:ThreadAstarthappens-beforeThreadBstartjoin规则:如果线程A执行操作ThreadB.join()并成功返回,则线程B中的任何操作happens-before线程A从ThreadB.join()操作成功返回。结束语这一段的内容可能没有之前那么容易理解,比较抽象,所以这篇文章也有不足之处。大家可以自己多查一些资料,全面了解一下。下一节,我将带大家深入了解Java的volatile。