Java重排序对多线程的影响

本文探讨了Java虚拟机在代码优化过程中进行的编译器优化、指令级并行和内存系统重排序对多线程的影响。重排序可能导致内存可见性问题,但Java内存模型(JMM)通过内存屏障来防止特定类型的处理器重排序。在多线程环境中,无数据依赖性的指令可能会被重排序,导致预期行为的改变。为解决这个问题,可以使用内存屏障或同步原语来确保线程间的正确同步,保证操作的相对顺序,从而避免重排序带来的影响。
摘要由CSDN通过智能技术生成

上一次的发文远在两个月前了,算是经历了一段低糜期,本来打算的更新一直断更到现在。还是好好学习吧,努力的人运气一定不会差的。这一篇文章来谈一谈Java虚拟机对代码优化带来的影响吧。

我们知道无论什么语言,最后驱动计算机的那一定是汇编,Java代码编译后会变成一段字节码,通过Java虚拟机的类加载机制ClassLoader加载到虚拟机里面,最后便是把它转化成汇编指令。通过JMM(Java内存模型)实现内存的可见性。鉴于篇幅这里不深入JMM。

从Java源代码到最终执行的指令序列,会经历下面几种重排序,重排序的作用可想而知是为了让提高Java的并行效率。
1. 编译器优化重排序
2. 指令级并行重排序
3. 内存系统重排序
所以这里就会产生内存可见性问题,当然JMM可以通过插入内存屏障来禁止特定类型的处理器重排序。
当然它的重排序不会是随心所欲的,JMM保证
1. 但单一线程里面,重排序不会影响程序运行的结果。
2. 如果程序存在数据依赖,那么它将不会被重排序,那什么叫做数据依赖呢?就是这三种情况:a>写一个变量之后,再读这个位置 b>写一个变量之后,再写这个变量 c>读一个变量之后,再写这个变量
所以这篇文章就记录一下,Java重排序对多线程的影响。
我们来看这样一段代码

class Handler{
    private boolean temp = false;
    int a = 0;
    public void write(){
        a = 1;
        temp = true;
    }

    public void read(){
        if(temp){
            int i = a * a;
        }
    }
}



我们来分析一下,我如果开了一个线程去write(),再开一个线程去read()结果会是预想的那样吗?
答案是不一定。
首先,write()方法里面的两句话是没有数据依赖性的,意味着他们可以重排序,也就是说我的temp的值可以先于a进行修改。如果在a修改之前我的read线程执行到了if判断,那么我if判断里面的a值是不是不对了呢。与原先的预想,我先改a的值,改了之后我才能读的预期效果不相符合了。
那么,我们要怎么样来避免这样的重排序呢?在JMM层面就要用到内存屏障,在应用层面我们只需要用同步原语即可。
修改后的代码

class Handler{
    private boolean temp = false;
    int a = 0;
    public synchronized void write(){
        a = 1;
        temp = true;
    }

    public synchronized void read(){
        if(temp){
            int i = a * a;
        }
    }
}

我们再来分析一下,这样做就实现了同步,什么叫做同步?
同步就是控制不同线程间操作发生相对顺序的机制。
回到上一个场景,就是只有在write()执行完之后read()才执行,那么write里面的重排序也就对read没有什么影响了。

总结:当一个变量跨进程使用时候,特别要注意重排序对它是否有影响

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值