当前位置: 首页 > 网络应用技术

高并发症吗?可能是汇编优化导致有序问题

时间:2023-03-08 22:22:48 网络应用技术

  订购是指:按照代码顺序实施。

  为了更受欢迎,该代码将以指定的顺序执行。例如,按程序的顺序,首先执行第一行代码,然后执行第二行代码,然后执行第三行代码,等等。如下所示。

  为了优化程序的执行性能,编译器或解释器有时会更改程序的顺序。但是,编译器或解释器的执行顺序可能会导致意外问题!

  在单线线程下,指令重排序可以确保最终执行结果与订单订单的结果一致,但是多线程中会出现问题。

  如果指令已排序,则程序可以首先执行第一行代码,然后执行第三行代码,然后执行第二行代码,如下所示。

  例如,以下三行代码。

  当CPU有要排序的指令时,x = 1的两行的两行和y = 2在z = x+ y代码上,x = 1和y = 2的顺序不一定是。在单线线程下没有问题,但不一定在多线程中。

  为了优化程序,CPU将对程序的说明进行排序。目前,程序执行顺序和代码的顺序可能不一致,这可能会导致有序问题。

  在Java程序中,一个经典的情况是使用双重检查机制创建单身对象。例如,在以下代码中,在getInstance()方法中获取对象实例时,首先确定实例对象是否为空。如果是空的,请锁定当前类的类对象,并检查实例是否为空。如果实例对象静止,请为实例对象创建一个实例。

  如果编译器或解释器不会优化上述程序,则整个代码的执行过程如下所示。

  注意:为了使每个人更清楚流程图的执行顺序,我已经标记了上图中的数字,以阐明线程A和线程B的执行顺序。

  假设此时,线程A和线程B的两个线程同时调用getInstance()方法以获取对象实例。这两个线程还会发现实例对象为空。在这个时间上,线程获取锁。在这里,我们假设线程A获取锁。线程B正在等待,因为它没有获得。这次,线程B唤醒,线程B试图再次获得锁。锁定成功后,线程B检查了此时的实例对象不再为空,并且线程B不再创建实例对象。

  以上所有内容看起来都很完美,但是所有这些的前提是编译器或解释器不会优化程序,也就是说,CPU并未对程序进行排序。实际上,所有这些都是这样。

  当在真正的高频率环境中运行上述环境以获取实例对象时,由于编译器或解释器对程序的优化或解释器的优化,创建对象的新操作将遇到问题。问题的根源在于以下行。

  对于上述代码,将有三个与之相对应的CPU说明。

  1.分配内存空间。

  2.初始化对象。

  3. pind到实例引用内存空间。

  正常的执行CPU指令订单为1-> 2-> 3,CPU后的执行顺序重新序列时可能为1-> 3->2。在此点,会有问题。

  当程序重新序列后的CPU执行顺序为1-> 3-> 2时,我们调用线程A和线程B调用getInstance()方法以获取对象实例的两个步骤。

  【第一步】

  (1)假设线程A和线程B同时输入第一个IF条件判断。

  (2)假设Thread A首先获得同步锁定并输入同步代码块。此时,由于实例对象为null,然后执行actence = new SingleInstance()语句。

  (3)执行实例= new SingleInstance()语句时,线程A将在JVM中打开空白存储空间。

  (4)将引用实例线程到空白存储空间。当对象初始化它时,会发生线程开关。线程A释放同步锁,CPU切换到线程。

  (5)线程B输入同步代码块,并读取线程A返回的实例对象。此时,此实例不是null,而是不初始化对象。这是一个空的对象。在这一点上,如果您使用实例!交叉截面,则线程B可能会出现问题

  【第二步】

  (1)线程A首先进入IF条件判断,

  (2)螺纹A获得同步的锁,并进行第二次有条件判断。目前,实例为null并执行实例= new SingleInstance()语句。

  (3)线程A在JVM中打开空白的存储空间。

  (4)将引用实例线程到空白存储空间。当对象初始化它时,会发生线程开关,并且CPU开关到线程B。

  (5)线程B执行第一个如果判断,并发现实例对象不是null,但是目前的实例对象没有初始化操作,则是一个空对象。如果线程B直接使用此实例对象,则可以有问题!交叉开采

  在第二步中,即使在切换线程时螺纹A尚未释放锁,发现线程B是第一个如果第一个判断,则发现该实例不再为空,并且返回实例直接无需尝试获得同步锁。

  我们可以将上述过程简化为下图。

  各种怪异问题的根本原因有三个引起并发编程的根本原因:可见性问题是由缓存引起的,由线程切换引起的原子问题以及由汇编和优化引起的有序问题。我们了解这三个问题的原因,可以从根本上看帮助我们更好地编写高并发程序。

  本文分享了华为的真诚云社区,作者:宾赫。