设计模式——单例模式中的问题及优化方法

单例模式是一种简单常用的设计模式,但是越简单的东西,其中包含的东西越不能忽略。

首先是线程不安全的单例模式:

public class UnsafeLazyInitialization {    
    private static Instance instance;
    
    public static Instance getInstance(){
        if(instance==null)              //1:A线程执行
            instance = new Instance();  //2:B线程执行
        return instance;
    }
    //构造器私有化
    private UnsafeLazyInitialization() {
    }
}

假设A线程执行代码1的同时,B线程执行代码2,线程A可能会看到instance引用的对象还没有完成初始化,即线程不安全。

下面使用synchronized关键字使上面的代码线程安全:

public class SafeLazyInitialization{
    private static Instance instance;

    public synchronized static Instance getInstance(){
        if(instance == null)
            instance = new Instance();
        return instance;
    }

    private SafeLazyInitialization() {
    }
}

上面的代码使线程安全了,但是导致了另一个问题,由于getInstance()方法做了同步处理,synchronized将导致性能开销;

如果getInstance()被多个线程频繁调用,将会导致程序执行性能下降。

不过办法总是比问题多的,下面将使用双重检查锁定来解决这个问题:

public class DoubleCheckedLocking{
    private static Instance instance;
    public static Instance getInstance(){
        if(instance == null){                                //1:第一次检查
            synchronized (DoubleCheckedLocking.class){       //2:加锁
                if (instance==null){                         //3:第二次检查
                    instance = new Instance();               //4:问题的根源出在这里
                }
            }
        }
        return instance;
    }
    //构造器私有化
    public DoubleCheckedLocking() {
    }
}

以上的代码是不是看起来完美无缺了呢,表面上看起来肯定使完美的,但这仍然是一个错误的优化。

问题的根源:instance = new Instance();

这一行代码可以分为如下的三行代码:

memory = allocate(); //1:分配对象的内存空间;
ctorInstance(memory);//2:初始化对象;
instance = memory;  //3:设置instance指向刚分配的内存地址

上面3行伪代码中的2和3之间可能会被重排序,如下:

memory = allocate(); //1:分配对象的内存空间;
instance = memory;  //3:设置instance指向刚分配的内存地址,此时对象还没有初始化
ctorInstance(memory);//2:初始化对象;

即当有线程在new Instance()时,其他线程在判断if(instance==null)时,可能判断不为空,但是此时的对象还没有完成初始化,最后导致问题。

啰嗦这么半天,下面是最后的解决方案:

①基于volatile的解决方案:

/**
 * 线程安全的懒汉单例模式
 */
public class SafeDoubleCheckedLocking{
    private volatile static Instance instance;

    public static Instance getInstance(){
        if(instance == null){
            synchronized (DoubleCheckedLocking.class){
                if (instance==null){
                    instance = new Instance();  //instance为volatile变量,现在就没问题了
                }
            }
        }
        return instance;
    }

    private SafeDoubleCheckedLocking() {
    }
}

②基于类初始化的解决方案:

/**
 * 饿汉单例模式
 */
class InstanceFactory{
    private static class InstanceHolder{
        public static Instance instance = new Instance();
    }

    public static Instance getInstance(){
        return InstanceHolder.instance; //这里将导致InstanceHolder类被初始化
    }

    private InstanceFactory() {
    }
}
以上就是单例模式的全部解析了,最后需要提醒的就是,单例的构造器一定要私有化!!!

猜你喜欢

转载自blog.csdn.net/crazer_cy/article/details/80380142
今日推荐