Java重排序

之前听公司讲座说到的设计模式,经典的懒汉式单例模式会有重排序问题,当时不是很理解,后来深入学了JVM终于恍然大悟,这里做个总结分享。
重排序排序的就是操作指令的顺序,改变了指令的执行顺序。重排序首先要知道字节码.class文件,它就是JavaC编译后的那个字节码文件,它里面有操作指令的执行顺序,程序计数器就是根据字节码的操作指令的顺序进行寻址查找属性和方法进行操作。JVM会自行判断,把速度快的逻辑简单的代码先执行。
但是有的情况下,JVM会自己对执行指令进行一些优化,比如我们知道for循环重复读取一个值,它会直接从缓存中读取,这时候值就算发生改变JVM也没及时发现。就要加volatile关键字修饰那个属性值,volatile关键字简单来说,就是不让JVM进行优化。
重排序问题也是JVM做的优化,但是变成了好心做坏事。
比如单例模式下,我们都知道创建对象过程中,分配空间是在加载阶段,而里面有什么属性什么方法,都是初始化阶段才有的。JVM优化后就可能直接return了一个全部是null的只是分配了内存空间但是没有初始化的对象。
来看简单的懒汉式单例模式例子:
public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  // (1)线程A进来了
    if (instance == null) {               // (3)然后再执行new Singleton
        instance = new Singleton();      
    }  
    return instance;  // (2)JVM优化重排序,直接先返回了instance对象
    }  
}
比如有个方法A先执行,再给属性B附值,最后B return,但是A执行复杂,JVM觉得A可能不影响B的return,就先附值return B,再慢慢跑A方法,这就是操作指令的执行顺序变了(字节码文件可以看到的)。
所以,懒汉式单例模式必须加入双重锁校验和volatile关键字,使得 == null 判断每次都会去读取最新的singleton,而不是读缓存中的那个,这就解决了JVM的重排序问题。
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {           // 第一次检查对象是否为空
        synchronized (Singleton.class) {  
        if (singleton == null) {      // 第二次检查对象是否为空
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;                 // 最后才能return
    }  
}
具体的测试代码在网上也很多,都可以证明JVM的重排序是真实存在的,而且一发生了问题就很难排查,除非不怕辛苦一行行看字节码操作指令的一行行执行。

鞠躬。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值