指令重排

问题描述
你写的代码很可能根本没按你期望的顺序执行,因为编译器和CPU 会尝试重排指令使得代码更快地运行,因为CPU的运行速度大于内存的读写速度。
**问题解释: **
happend-before
执行代码的顺序可能与编写代码不一致,即虚拟机优化代码顺序,则为指令重排happen-before即:编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。

  • 在虚拟机层面,为了尽可能减少内存操作速度远慢于CPU运行速度所带来的CPU空置的影响,虚拟机会按照自己的一些规则将程序编写顺序打乱——即写在后面的代码在时间顺序上可能会先执行,而写在前面的代码会后执行——以尽可能充分地利用CPU。拿上面的例子来说:假如不是a=1的操作,而是a=new byte1024*1024,那么它会运行地很慢,此时CPU是等待其执行结束呢,还是先执行下面那句flag=true呢?显然,先执行flag=true可以提前使用CPU,加快整体效率,虽然这里有两种情况:后面的代码先于前面的代码开始执行;前面的代码先开始执行,但当效率较慢的时候,后面的代码开始执行并先于前面的代码执行结束。不管谁先开始,总之后面的代码在一些情况下存在先结束的可能。
  • 在硬件层面,CPU会将接收到的一批指令按照其规则重排序,同样是基于CPU速度比缓存速度快的原因,和上一点的目的类似,只是硬件处理的话,每次只能在接收到的有限指令范围内重排序,而虚拟机可以在更大层面、更多指令范围内重排序。
  • 当然编译器和处理器在重排这些指令的时候要根据一套的规则,那就是数据依赖,如果两个操作访问同一个变量,且这两个操作中有一个为写操作,此时这两个操作之间就存在数据依赖。数据依赖分下列三种类型:写后读a = 1;b = a;,写后写a = 1;a = 2;,读后写a = b;b= 1;
    上面的三种情况,只要 先后顺序颠倒,所得出来的数据结果就会被改变,所以处理器和编译器在指令重排的时候要遵守数据依赖的规则,重拍的时候不会改变原有的数据依赖,这一点与数据库的依赖略有所同。

计算机在执行指令的步骤使处理器更好的利用
1. 从内存中拿到指令
2. 指令进行解码,从寄存器中拿值
3. 计算结果
4. 将计算结果写回寄存器
eg:
指令代码编译解码为机器语言
sub=a+b;----------编译------------>ADD R1,R2->R3
c+=sub;------------编译------------>ADD R4,R3->R4
isDone=true;-----编译------------->ADD 1->R5
再从寄存器中拿值
a=2,b=3;
R1–>2
R2–>3
R3–>0
R4–>0
R5–>0
通过:ADD R1,R2->R3进入加法器计算,后返回R3的值
R1–>2
R2–>3
R3–>5
R4–>0
R5–>0
同理得出R4的值
问题就出在进行返回值得时候,由于缓存得运行速度不如CPU快,在还没有返回之前为提高cpu得效率,CPU进行下一条指令ADD 1->R5
如果在多线程中isDone与其他线程有关系,就会影响其他线程,如果isDone得true,false与上一条ADD R4,R3->R4有关系则会得出与我们预期得结果不一样。出现指令重排。
代码解释:

public class HappenBefore {
    //变量1
    private static  int a=0;
    //变量2
    private  static boolean flag=false;
    public static void main(String[] args) {
        for(int i=0;i<100;i++) {
            a = 0;
            flag = false;
			//修改数据
            Thread t1 = new Thread(() -> {
                a = 1;
                flag = true;
            });
            //输出数据
            Thread t2 = new Thread(() -> {
                if (flag) {
                    a *= 1;
                }
                //指令重排
                if (a == 0) {
                    System.out.println("happendbefore a->" + a);
                }
            });
            ;
            t1.start();
            t2.start();

            try {//线程插队//让t1先运行结束
                t1.join();
                t2.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:
在这里插入图片描述
此代码中当循环运行,给两个线程先后加了join线程合并,t1先执行完,在执行t2,t1线程为修改数据,在修改a1,并返回之前,cpu等不了了,就开始执行t2,此时a的值还是为0.所以执行了if(a0)在输出之前,a数据已经改好了为1,所以此时输出happendbefore a->1。这就是一个典型得指令重排。由于视频解释不详,想了好长一会!!!!!!

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值