美文网首页
单例模式的演进

单例模式的演进

作者: herohua | 来源:发表于2020-01-15 10:11 被阅读0次

Singleton是指仅仅被实例化一次的类。Singleton通常被用来代表一个无状态的对象,或者那些本能上唯一的系统组件。实现Singleton有两种常见的方法。分别是懒汉式和饿汉式。这两种方法都要保持构造器为私有的,并导出共有的静态成员,一遍允许客户端能够访问该类的唯一实例。

1. 最简单又安全的写法--饿汉式

public class SingletonObject1 {

    private final static SingletonObject1 INSTANCE = new SingletonObject1();

    // 私有化无参构造器
    private SingletonObject1() {
    }

    public static SingletonObject1 getInstance() {
        return SingletonObject1.INSTANCE;
    }
}

饿汉式由于在类加载的时候已经将唯一实例初始化,所以没有多线程安全问题。
但是没有使用懒加载思想,对于那些不长使用的对象,不需要再类初始化的时候就将唯一实例初始化,在使用该对象的时候再进行初始化会更好。相对应的就是懒汉式单例模式。

2. 懒加载思想的实践-懒汉式

2.1 版本一

public class SingletonObject21 {

    private static SingletonObject2 INSTANCE;

    private SingletonObject2() {
    }
    
    public static SingletonObject2 getInstance() {

        if (INSTANCE == null)
            INSTANCE = new SingletonObject2();

        return INSTANCE;
    }
}

公有静态方法getInstance提供了获取唯一实例的唯一途径(构造器私有化),在该方法里,首先会进行一次空判断,如果唯一实例还没有初始化,则进行初始化,否则说明实例已经初始化,直接返回。也就是说,只有第一次访问该方法的时候,才会执行初始化工作,后面的访问直接就能获取已经初始化好的实例。这种方式体现了懒加载思想。但是再并发场景下会存在多线程安全问题,当多个线程都进行过判空但都没有实例化之前,可能生成多个实例,这与单例模式是相悖的。

2.2 版本二

public class SingletonObject22 {

    private static SingletonObject2 INSTANCE;

    private SingletonObject2() {
    }

    public static synchronized SingletonObject2 getInstance() {

            if (INSTANCE == null)
                INSTANCE = new SingletonObject2();

        return INSTANCE;
    }
}

该版本通过在公有静态方法getInstance上添加synchronized关键字确保同一时刻只能有一个线程访问该方法,这样的确保证了线程安全,但是在方法级别上使用synchronized关键字范围过大,每次访问该方法都需要加锁,事实上只有唯一实例还没有初始化之前需要加锁。当唯一实例已经初始化之后,后面的访问只是只读操作,不需要加锁,所以该版本性能上有问题,需要缩小使用synchronized代码块。

2.3 版本三

public class SingletonObject23 {

    private static SingletonObject2 INSTANCE;

    private SingletonObject2() {
    }

    // double check
    // may exists NullPointerException
    public static SingletonObject2 getInstance() {
        if (INSTANCE == null) {
            synchronized (SingletonObject2.class) {
                if (INSTANCE == null)
                    INSTANCE = new SingletonObject2();
            }
        }
        return INSTANCE;
    }
}

使用synchronized代码块缩小了加锁单位,当唯一实例没有初始化时,代码逻辑才会走到synchronized代码块。这里需要注意的是,有可能多个线程都已经进行了第一次判空,然后去争抢锁,如果没有第二次判空,那么多个线程会依次拿到锁然后进行初始化实例,这样有产生了多个实例,所以需要double check。该版本既解决了版本一的线程安全问题,又解决了版本二的性能问题,又是懒加载,似乎是一种完美的方法,其实不然,这里有可能出现空指针异常。

2.4 版本四

public class SingletonObject24 {

    // can't exists NullPointerException
    private static volatile SingletonObject3 INSTANCE;

    private SingletonObject3() {
    }

    // double check
    public static SingletonObject3 getInstance() {
        if (INSTANCE == null) {
            synchronized (SingletonObject3.class) {
                if (INSTANCE == null)
                    INSTANCE = new SingletonObject3();
            }
        }
        return INSTANCE;
    }
}

使用volatile关键字确保不会发生指令重排序,从而避免空指针异常。

3. 静态成员类式

public class SingletonObject3 {

    private SingletonObject3() {
    }

    private static class InstanceHolder {
        private final static SingletonObject3 INSTANCE = new SingletonObject3();
    }

    public static SingletonObject3 getInstance() {

        return InstanceHolder.INSTANCE;
    }
}

静态成员类是最简单的一种嵌套类,它存在的目的只是为它的外围类提供服务。最好把它看作是普通的类,,只是碰巧被声明在一个类的内部而已,外围类被加载的时候,只会初始化静态变量、静态代码块、静态方法,但不会静态成员类。当调用静态成员类的静态成员时,静态成员类才会被加载。这里调用公有静态方法getInstance时会调用静态成员类的INSTANCE变量,此时静态成员类InstanceHolder才会进行加载,从而唯一实例INSTANCE也得到初始化。这里既保证了多线程安全,又使用了懒加载机制。

3. Effective Java推荐的方法-枚举式

public class SingletonObject4 {

    private SingletonObject4() {
    }

    private enum SingletonEnum {
        INSTANCE;

        private final SingletonObject4 instance;

        SingletonEnum() {
            instance = new SingletonObject4();
        }

        public SingletonObject4 getInstance() {
            return instance;
        }
    }


    public static SingletonObject4 getInstance() {

        return SingletonEnum.INSTANCE.getInstance();
    }
}

枚举类型只会被装在一次,能保证线程安全。而且枚举类型能保证反射攻击和序列化攻击,是EffectiveJava极力推崇的一种单例实现方式。

相关文章

  • 单例模式的演进

    Singleton是指仅仅被实例化一次的类。Singleton通常被用来代表一个无状态的对象,或者那些本能上唯一的...

  • 【设计模式】单例模式

    单例模式 常用单例模式: 懒汉单例模式: 静态内部类单例模式: Android Application 中使用单例模式:

  • Android设计模式总结

    单例模式:饿汉单例模式://饿汉单例模式 懒汉单例模式: Double CheckLock(DCL)实现单例 Bu...

  • Telegram开源项目之单例模式

    NotificationCenter的单例模式 NotificationCenter的单例模式分析 这种单例模式是...

  • 设计模式之单例模式详解

    设计模式之单例模式详解 单例模式写法大全,也许有你不知道的写法 导航 引言 什么是单例? 单例模式作用 单例模式的...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • IOS单例模式的底层原理

    单例介绍 本文源码下载地址 1.什么是单例 说到单例首先要提到单例模式,因为单例模式是单例存在的目的 单例模式是一...

  • 常见的设计模式

    创建型模式 结构型模式 行为模式 单例模式 单例模式的优点 常见的五种单例模式实现方式 单例带来的问题 如何选择 ...

  • 单例模式之枚举类enum

    通过枚举实现单例模式 枚举类实现单例模式的优点 对于饿汉式单例模式和懒汉式单例模式了解的同学,使用以上两种单例模式...

网友评论

      本文标题:单例模式的演进

      本文链接:https://www.haomeiwen.com/subject/afrzactx.html