设计模式--单例模式--DCL

3 DCL懒汉式 (双重检测)

DCL是在懒汉式的基础上修改的

懒汉式

public class SingletonDemo2 {
//优缺点
//    优  线程安全  可以延迟加载 
//    缺点  效率不高
    //    私有化构造器
    private SingletonDemo2() {}
    //    创建但不初始化
    private static SingletonDemo2 INSTANCE;
    public static synchronized SingletonDemo2 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SingletonDemo2();
        }
        return INSTANCE;
    }
}

有synchronized可以保证线程安全 但是只有每次同步请求 会造成性能上的浪费

那我们吧 synchronized 放到里面 并顺带加一个判断

public class SingletonDemo3 {
    //    1. 私有化构造器
    private SingletonDemo3() {}
    // 2.初始化单例
    private static SingletonDemo3 instance ;
//    3.全局访问方法
    public static SingletonDemo3 getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo3.class) {
                if (instance == null) {
                    instance = new SingletonDemo3();
                }
            }
        }
        return instance;
    }
}

现在假设有两个线程 线程一 线程二

线程一抢先进入getInstance()方法 并获取到了类锁 开始执行 synchronized 的代码块儿 这时候instance 是空的 要new给线程一

这时候 创建实例 会有几个过程

  • 分配内存
  • 初始化构造器
  • 指向内存地址 这时候instance才不是空

由于指令重排 可能这三个步骤就会不是这样 可能变成这样

  • 分内存
  • 指向内存地址
  • 初始化构造器

可看到 没有执行到 初始化构造器 的时候instance已经不为空了 但是又没有初始化 如果 这时候线程二进到了getInstance() 那么就会直接判定instance不为空 那就直接被线程二拿走用了 但是这时候又没有初始化 所以

就会报错

给instance加一个volatile关键字


public class SingletonDemo3 {
    //    1. 私有化构造器
    private SingletonDemo3() {}
    // 2.初始化单例
    private static volatile SingletonDemo3 instance ;
//    3.全局访问方法
    public static SingletonDemo3 getInstance() {
        if (instance == null) {
            synchronized (SingletonDemo3.class) {
                if (instance == null) {
                    instance = new SingletonDemo3();
                }
            }
        }
        return instance;
    }
}

就可以了

你可能感兴趣的:(设计模式)