单例模式解析

单例模式解析

​ 单例模式是设计模式中使用最多的模式之一,它是一种对象的建立模式,用于产生一个对象的实例,确保系统中一个类只有一个实例。在Java语言中,单例模式有如下好处:java

  • 对于频繁使用的对象,单例省略了建立对象的时间,对于重量级对象而言,极大减小了系统开销。
  • 因为new的次数减小,所以减小了系统对内存的使用频次,减轻了GC压力,缩短了GC停顿时间。

所以对于系统的关键组件和频繁使用的对象,能够设计为单例模式,减小系统的开销,提升系统性能。设计模式

​ 单例模式的参与者不多,只有单例类和使用者,关系表以下:多线程

角色 做用
单例类 提供单例的工厂,返回单例
使用者 获取并使用单例

​ 类图以下:函数

实现方式一(饿汉):

单例模式的核心在于经过一个接口返回惟一实例化对象,简单实现以下:性能

/** * 单例模式--饿汉(没法作到延时加载) */
public class HungrySingleton {

    private static HungrySingleton instance = new HungrySingleton();

    /** * 私有构造方法 */
    private HungrySingleton(){
        System.out.println("建立了对象");
    }

    /** * 对外暴露的获取惟一实例的接口 * @return */
    public static HungrySingleton getInstance(){
        return instance;
    }

    /** * 序列化,反序列化保证单例 * @return */
    private Object readResolve(){
        return instance;
    }
}
复制代码

原理:单例类必须私有化构造方法,保证不会在系统其余地方调用,其次instance成员变量和getInstance()方法必须是static修饰的。优化

注意:单例模式的这种实现方式很是简单,十分可靠,惟一不足是没法作到延时加载。假设单例的建立过程十分缓慢,因为instance的成员变量是由static修饰的,在JVM加载单例类的时候,单例对象就会存在,若是单例类还在系统中扮演别的角色,那么系统中任何使用单例类的地方都会初始化这个单例对象,而无论是否被用到。spa

实现方式二(懒汉):

为了解决上述问题,并提升系统在相关函数调用的反应速度,就须要加入延时加载机制,懒汉模式。线程

/** * 单例模式--懒汉(效率低,延时加载) */
public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton() {
        System.out.println("LazySingleton is create");
    }

    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            return new LazySingleton();
        }
        return instance;
    }
}
复制代码

原理:首先,静态成员变量instance赋值为null,确保系统启动时没有额外负载,其次在getInstance()方法中判断当前单例instance对象是否存在,存在则返回,不存在建立单例对象。设计

注意:getInstance()必须是线程同步的,不然在多线程条件下,当线程1新建单例完成赋值前,线程2可能判断instance为null,线程2也建立了单例对象,致使多个实例被建立,所以同步关键字是必须的。使用上述单例模式的实现方式,虽然实现了延时加载,可是和第一种实现(饿汉)相比,引入了同步关键字,所以在多线程场景下,加载速度远远大于第一种实现方式,影响系统性能。code

实现方式三(内部类):

继续改进,建立内部类:

/** * 使用内部类来维护单例的实例,当StaticSingleton被加载时候,内部类并无被初始化 * (instance并无被初始化),调用getInstance()才会被初始化。 */
public class StaticSingleton {

    private StaticSingleton(){
        System.out.println("StaticSingleton is create");
    }

    /** * 内部类,建立单例对象 */
    private static class StaticSingleHolder{
        private static StaticSingleton instance = new StaticSingleton();
    }

    public static StaticSingleton getInstance(){
        return StaticSingleHolder.instance;
    }
}
复制代码

原理:在这个实现中,使用内部类来维护单例实例,当StaticSingleton被加载的时候,内部类没有被初始化,能够确保StaticSingleton加载到JVM中,不会初始化单例类,当调用getInstance()时才会加载StaticSingleHolder,初始化instance对象,同时因为实例的创建是在类加载时完成的,对线程友好,getInstance()不须要使用同步关键字。

注意:使用内部类实现的单例,既能够实现延时加载也避免使用同步关键字,是比较完善的实现。可是若是经过反射机制强行调用私有构造方法,就会生成多个单例。同时序列化和反序列化可能破坏单例(饿汉代码readResolve()方法),场景很少见,若是存在,多加注意。

[^《Java性能程序优化 让你的Java程序更快、更稳定》 葛一鸣]

相关文章
相关标签/搜索