zoukankan      html  css  js  c++  java
  • 单例模式双重检验锁的volatile和两次判空

    首先是代码,经典的双重锁写法

    public class Singleton {
        private volatile static Singleton Instance;
        private Singleton(){}
        public static Singleton getInstance(){
            if(Instance == null){
                synchronized(Singleton.class){
                    if(Instance == null){
                        Instance = new Singleton();
                    }
                }
            }
            return Instance;
        }
    }
    
    

    先说为什么需要两次判空的原因?

    第一次判断是为了验证是否创建对象,避免多线程访问时每个线程都加锁,提升效率第二次判断是为了避免重复创建单例,因为可能会存在多个线程通过了第一次判断在等待锁,来创建新的实例对象。
    例如:有三个线程,A与B同时调用getSingleton时,判断第一个if都为空,这时A拿到锁,进行第二层if判断,条件成立new了一个对象;由于Synchronized原因,B在外层等待,A创建完成,释放锁,B拿到锁,进行第二层if判断,条件不成立,结束释放锁。C调用getSingleton时第一层判断不成立,直接拿到singleton对象返回,避免进入锁,减少性能开销。

    在说说为什么有了Synchronized却还需要volatile去修饰Instance。

    volatile修饰变量只是为了禁止指令重排序,因为在 Instance = new Singleton(); 创建对象时,底层会分为四个指令执行:(下面是正确的指令执行顺序)
    ①、如果类没有被加载过,则进行类的加载
    ②、在堆中开辟内存空间 adr,用于存放创建的对象
    ③、执行构造方法实例化对象
    ④、将堆中开辟的内存地址 adr 赋值给被volatile修饰的Instance引用变量
    
    如果Instance引用变量不使用volatile修饰的话,则可能由于编译器和处理器对指令进行了重排序,导致第④步在第③步之前执行,此时Instance引用变量不为null了,但是Instance这个引用变量所指向的堆中内存地址中的对象是还没被实例化的,实例对象还是null的;那么在第一次判空时就不为null了,然后去使用时就会报NPE空指针异常了。

    原文链接:https://blog.csdn.net/weixin_45398467/article/details/108893962

  • 相关阅读:
    BZOJ 2527 Meteors 整体二分
    BZOJ 1176: [Balkan2007]Mokia
    DP杂题2
    点分治
    一些图论模板
    一些字符串的题
    斐波那契+线段树
    BZOJ 2957楼房重建
    POJ
    BZOJ 2002 弹飞绵羊
  • 原文地址:https://www.cnblogs.com/SimonHu1993/p/14630811.html
Copyright © 2011-2022 走看看