今天看啥  ›  专栏  ›  nzdxwl

设计模式 - 单例

nzdxwl  · 简书  ·  · 2019-11-15 23:23

文章预览

当我们希望某个类在应用程序执行过程中有且仅有一个实例的时候,就可以用到单例模式了。

双重检查加锁方式:

// final类不被继承
public final class DCLSingleton {
    private static volatile DCLSingleton instance;    

    //单例中我们要获取的资源
    public Resource resource = null;

    private DCLSingleton() {
        //...这里进行resource的创建
        resource = ... ;
    }
    
    /**
     * 双重校验,当instance不为null时,获得当前类的锁,然后再次判断instance是否等于null:
     * 这是为防止在多个线程调用情况下,当线程A判断instance为null,获得类锁后,创建新的实例,
     * 此时如果新的实例还未创建完毕时,线程B判断instanc为null,在进行类锁获取时,线程A刚好创建完实例释放类锁,并由线程B获得,
     * 那么如果没有第二个等于null的判断的话,则又会创建另一个新的实例。所以这种单例模式就称为双重检查加锁模式。
     * @return
     */
    public static DCLSingleton getInstance() {
        if(instance == null) {
            synchronized(DCLSingleton.class) {
                if(instance == null) {
                    instance = new DCLSingleton();
                }
            }
        }
        
        return instance;
    }
}

上面的方式看起来做了很充分的检查,但在多线程情况仍有可能出现空指针异常:
我们在私有构造器中进行资源实例化,还有单例自身,根据JVM运行时指令重排序和Happens-Before规则,这两者的实例化顺序并无前后关系的约束,有可能单例实例最先被实例化,而资源尚未完成实例化,此时其他线程此时调用getInstance()方法获得单例实例,如果直接调用resource方法则会抛出空指针异常。

以下是改进后的单例实现:
Holder方式:

public final class HolderSingleton {

    // 私有构造器
    private HolderSingleton() {
        // ...资源实例创建
    }

    // 私有静态类
    private static class Holder {
        private static HolderSingleton instance = new HolderSingleton();
    }

    //通过内部静态类来创建实例
    public static HolderSingleton getInstance() {
        return Holder.instance;
    }
    //单例中的资源    
    private static Resource resource = null;

    public static Resource getResource() {
        return resource;
    }

}

instance被放置到静态内部类Holder中,HolderSingleton类的初始化不会创建实例,当Holder被主动引用时才创建。

………………………………

原文地址:访问原文地址
快照地址: 访问文章快照
总结与预览地址:访问总结与预览