指令重排现象,多线程情况下,你的代码执行顺序可能不是顺序执行,结果会不一致

一、思考多线程情况下,程序执行顺序是否是按顺序执行

  1. 首先定义x = 0; y = 0; a = 0; b = 0;
  2. 然后思考a = 1;x = b;两行代码谁先执行问题?

二、实战测试

2.1 测试逻辑

  • 首先默认为x = 0; y = 0; a = 0; b = 0;然后开启两个线程;
  • 线程1执行:a = 1;x = b;
  • 线程2执行:b = 1;y = a;
  • 有且只有x = b,y = a两个同时先执行,才会出现x=y=0。所以测试是否存在x=y=0观察指令是否会出现重排现象。

2.2 代码测试

 public class OrderTest {
    private static int x = 0, y = 0;
    private static int a = 0, b = 0;

    public static void main(String[] args) throws InterruptedException{
        for(long i = 0; i < Long.MAX_VALUE; i++){
            x = 0; y = 0; a = 0; b = 0;
            //CountDownLatch是一个同步工具类,它通过一个计数器来实现的,初始值为线程的数量。
            // 每当一个线程完成了自己的任务,计数器的值就相应得减1。
            // 当计数器到达0时,表示所有的线程都已执行完毕,然后在等待的线程就可以恢复执行任务。
            CountDownLatch countDownLatch = new CountDownLatch(2);
            Thread one = new Thread(new Runnable() {
                @Override
                public void run() {
                    a = 1;
                    x = b;
                    // 每调用一次计数器值-1,直到count被减为0,代表所有线程全部执行完毕
                    countDownLatch.countDown();
                }
            });

            Thread two = new Thread(new Runnable() {
                @Override
                public void run() {
                    b = 1;
                    y = a;
                    countDownLatch.countDown();
                }
            });

            one.start();
            two.start();
            //等待计数器变为0,即等待所有异步线程执行完毕
            countDownLatch.await();
            if(x == 0 && y == 0){
                //x=y=0 只能是x = b;y = a;这两个先执行
                System.out.println("执行次数"+i+"发现x=y=0");
                break;
            }

        }
    }

}

2.4 测试结果

在1059079(100万)发现了指令重排现象。
在这里插入图片描述

三、指令重排现象

概念
指令重排序是指源码顺序和程序顺序不一样,或者说程序顺序和执行的顺序不一致,重排序的对象是指令。
指令重排序是编译器处于性能考虑,在不影响程序(单线程程序)正确性的条件下进行重新排序。指令重排序不是必然发生的,指令重排序会导致线程安全问题。指令重排序也被称为处理器的乱序执行,在这种情况下尽管指令的执行顺序可能没有完全按照程序顺序执行,但是由于指令的执行结果的提交(反应到寄存器和内存中),仍然是按照程序顺序来的,因此处理器的指令重排序并不会对单线程的正确性产生影响。指令重排序不会对单线程程序的正确性产生影响,但他可能导致多线程程序出现非预期结果。

四、如何解决

使用volatile的内存屏障功能。

使用volatile修饰的变量,在读或写之后会形成内存读写屏障的效果。
写屏障会确保指令重排序时,不会将写屏障之前的代码排在写屏障之后。
读屏障会确保指令重排序时,不会将读屏障之后的代码排在读屏障之前。

在1059079(100万)发现了指令重排现象。
我们用volatile修饰一下x,y,a,b变量,修改执行次数,在1059079(100万)后再加000,1059079000(10亿)再次运行观察。即
在这里插入图片描述
等了30分钟还没执行结束,也没出现x=y=0,说明volatile成功屏障了指令重排现象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

怪异的bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值