双重锁校验单例模式中的volatile

分析双重锁校验单例模式中的volatile为什么不可以省略。

public class Singleton_05 {
    /**
    * 注意此处volatile关键字不可少
    */
    private static volatile Singleton_05 instance;
    private Singleton_05() {
    }
    public static Singleton_05 getInstance(){
        // 如果对象已经存在,则直接返回
       if(null != instance) 
           return instance;
        // 否则新建对象,也要进行非空判断,防止多线程情况下,多个线程都判断空对象后准备创建对象
       synchronized (Singleton_05.class){
           if (null == instance){
               instance = new Singleton_05();
           }
       }
       return instance;
    }
}

以上的程序保证这个类的对象是单例的,有且仅有一个,但是如果没有加volatile关键字,则可能出现问题,原因在于指令重排。所谓指令重排是指在不影响程序运行结果的情况下,改变程序语句的执行顺序,减少中断,提高执行效率。对象创建过程中涉及三条指定,分别是给对象分配空间,赋值(先赋初值),建立关联。假设3条指定分别为a,b,c,在多线程情况下,如果一个线程过来,判断对象为空,则新建对象,a->b->c,完成对象创建,其他线程再过来的时候,如果在对象创建完成之前,则对象为空,执行创建,由于创建的程序加了锁,所以等待锁释放;如果在对象完成之后过来,则对象不为空,直接返回,由此,就实现了单例。

        但是,存在问题,由于synchronized不保证有序性,所以如果第一个线程创建对象的时候,发生了指令重排,在对象赋值之前建立了关联,即a->c->b,则会发生问题,此时另外一个线程过来,发现对象不为空,直接返回,但是此时返回的对象是错误的,未赋值的。

        解决上述问题的方法就是加volatile关键字,volatile可以保证内存可见性,禁止重排序,底层原理是在生成字节码的时候,在相关语句的前后加入内存屏障,具体可以另行了解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值