单例模式

单例模式(Singleton Pattern)是一种常见的设计模式,属于创建型设计模式(Creational Design Patterns),旨在确保一个类在整个系统中只有一个实例,并且提供一个全局的访问点来获取这个实例。

具体来说,单例模式通过限制类的实例化次数,确保系统中只有一个该类的对象存在。这通常用于以下场景:

  • 当系统需要共享一个资源,比如数据库连接、日志记录器或配置管理器时。
  • 确保一个类的实例被全局共享,避免资源浪费。

为什么要使用单例模式呢(什么时候应该使用单例模式)

单例模式的使用场景通常包括以下几种情况:

  1. 节约资源:当多个模块或组件需要共享同一个资源时。使用单例模式可以确保资源的一致性,避免重复创建和销毁实例。例如,数据库连接池、线程池等。

  2. 全局控制:保证只有一个实例,这样就可以严格的控制客户怎样访问它以及何时访问它,简单的说就是对唯一实例的受控访问。

  3. 控制实例数量:在某些情况下,系统中某个类的实例只能有一个,否则会导致逻辑错误或资源冲突。例如,配置管理器、日志记录器等。

  4. 懒加载:单例模式可以延迟实例的创建,直到第一次使用时才进行初始化,从而节省系统资源。

单例模式的基本要求

  1. 私有的静态实例变量:类内部维护一个静态的实例变量,用于保存唯一的实例。

  2. 私有构造函数:防止外部通过 new 关键字创建实例,确保实例的唯一性。

  3. 公有静态访问方法:提供一个静态的公共方法(通常命名为 getInstance),用于获取该类的唯一实例。

  4. 线程安全:在多线程环境下,确保单例模式的实现是线程安全的,避免多个线程同时创建多个实例。

单例模式的实现方法

  1. 懒汉式(Lazy Initialization)
    懒汉式单例模式在第一次调用 getInstance 方法时才创建实例。优点是延迟初始化,节省资源;缺点是在多线程环境下需要额外的同步机制来保证线程安全。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
    if (instance == null) {
    instance = new Singleton();
    }
    return instance;
    }
    }
  2. 饿汉式(Eager Initialization)
    饿汉式单例模式在类加载时就创建实例。优点是实现简单,线程安全;缺点是如果实例一直没有被使用,会造成资源浪费。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
    return instance;
    }
    }
  3. 双重检查锁(Double-Checked Locking)
    双重检查锁单例模式结合了懒汉式和饿汉式的优点,既实现了延迟初始化,又保证了线程安全。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
    if (instance == null) {
    synchronized (Singleton.class) {
    if (instance == null) {
    instance = new Singleton();
    }
    }
    }
    return instance;
    }
    }

单例模式的优缺点

优点:

  • 控制实例数量:确保系统中只有一个实例,避免资源浪费。
  • 全局访问点:提供了一个全局的访问点,方便其他模块获取实例。
  • 延迟初始化:可以在需要时才创建实例,节省系统资源。

缺点:

  • 难以扩展:单例模式通常难以扩展,因为它的实例是全局唯一的,扩展可能会导致设计复杂化。
  • 违反单一职责原则:单例类通常既负责业务逻辑,又负责实例管理,违反了单一职责原则。
  • 测试困难:由于单例模式的全局性,单元测试时可能会遇到困难,尤其是在需要模拟或替换单例实例时。

单例模式的应用场景

单例模式的应用场景非常广泛,在以下几个方面有着广泛的应用:

  • 配置管理:系统中通常只需要一个配置管理器来读取和保存配置信息。
  • 日志记录:日志记录器通常只需要一个实例来记录系统的运行日志。
  • 数据库连接池:数据库连接池通常只需要一个实例来管理数据库连接。
  • 缓存系统:缓存系统通常只需要一个实例来管理缓存数据。
  • 线程池:线程池通常只需要一个实例来管理线程资源。
  • 硬件访问:某些硬件资源(如打印机、扫描仪等)通常只需要一个实例来管理访问。