设计模式之单例模式

单例模式是一种创建型设计模式,其目的是确保一个类仅有一个实例,并提供一个全局访问点。这种模式常用于那些需要频繁创建和销毁的资源消耗较大的对象,或者需要严格控制实例数量的对象,例如数据库连接池、缓存、日志管理器等。

单例模式的核心要点包括:

  1. 确保单例:通过私有构造函数、静态内部类、双重检查锁定(DCL)、枚举等机制,防止外部创建额外的实例。
  2. 全局访问 :提供一个公共的静态方法(通常称为getInstance()),允许客户端在任何地方通过此方法获取单例对象。

单例模式有多种实现方式,以下是常见的几种:

1、饿汉式(静态常量)

在类加载时立即创建单例对象,线程安全,但可能造成资源浪费(如果单例在整个程序生命周期内并不需要,却在类加载时就被创建)。

Java代码示例:

java 复制代码
public class SingletonEager {
    private static final SingletonEager INSTANCE = new SingletonEager();

    private SingletonEager() {}

    public static SingletonEager getInstance() {
        return INSTANCE;
    }
}

2、懒汉式(线程不安全)

在首次调用getInstance()时创建单例对象,非线程安全。

Java代码示例:

java 复制代码
public class SingletonLazyUnsafe {
    private static SingletonLazyUnsafe instance;

    private SingletonLazyUnsafe() {}

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

3、懒汉式(线程安全,同步方法或同步块)

在首次调用getInstance()时创建单例对象,并通过同步方法或同步块确保线程安全,但每次获取实例都需要同步,影响性能。

Java代码示例:

java 复制代码
public class SingletonLazySynchronizedMethod {
    private static SingletonLazySynchronizedMethod instance;

    private SingletonLazySynchronizedMethod() {}

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

4、双重检查锁定(DCL,推荐使用)

getInstance()方法中进行两次检查(一次检查实例是否已创建,一次检查是否需要同步),既保证线程安全又减少了同步带来的性能损耗。

Java代码示例:

java 复制代码
public class SingletonDCL {
    private volatile static SingletonDCL instance;

    private SingletonDCL() {}

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

5、静态内部类

利用类加载机制保证线程安全,实现简单且性能良好。

Java代码示例:

java 复制代码
public class SingletonInnerClass {
    private SingletonInnerClass() {}

    private static class SingletonHolder {
        private static final SingletonInnerClass INSTANCE = new SingletonInnerClass();
    }

    public static SingletonInnerClass getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

6、枚举

JDK 1.5 引入的实现方式,简洁且天生线程安全。

Java代码示例:

java 复制代码
public enum SingletonEnum {
    INSTANCE;

    // 可以在此处添加其他方法和属性
}

使用过程中需要注意的问题

  1. 线程安全性:确保单例在多线程环境下的正确性,避免多个实例被创建。饿汉式、静态内部类和枚举实现天生线程安全,懒汉式和DCL需要特别关注线程安全问题。

  2. 序列化与反序列化 :若单例类实现了Serializable接口,可能通过反序列化创建新的实例。为防止这种情况,需在单例类中添加readResolve()方法,返回唯一的单例实例。

  3. 反射攻击:恶意代码可以通过反射机制调用私有构造函数创建新的实例。为防止这种情况,可以在构造函数中添加防御性编程,如抛出异常或设置标志位验证单例状态。

  4. 测试:单例模式可能导致单元测试困难,因为它与其他组件紧密耦合。可以考虑使用依赖注入框架或提供一个测试友好的构造函数(仅在测试环境中启用)来解决这个问题。

  5. 资源释放 :如果单例持有昂贵资源(如数据库连接、文件句柄等),需要确保在应用程序退出时正确释放资源,避免资源泄漏。可以使用java.lang.Runtime.addShutdownHook()注册关闭钩子,或使用try-finally语句确保资源清理。

  6. 性能:在高并发场景下,选择性能更好的实现方式,如DCL或静态内部类,避免不必要的同步开销。如果单例创建成本高,可以考虑使用延迟初始化(懒汉式)。

单例模式在确保一个类仅有一个实例的同时提供了全局访问点,适用于需要控制实例数量、共享资源或协调系统行为的场景。在实现和使用单例模式时,要注意线程安全性、序列化与反序列化、反射攻击、测试、资源释放和性能等方面的问题。根据实际需求选择合适的实现方式。

相关推荐
ok!ko1 小时前
设计模式之原型模式(通俗易懂--代码辅助理解【Java版】)
java·设计模式·原型模式
拉里小猪的迷弟2 小时前
设计模式-创建型-常用:单例模式、工厂模式、建造者模式
单例模式·设计模式·建造者模式·工厂模式
Lindsay.Lu丶3 小时前
valgrind & 单例模式的自动释放(多线程)
单例模式
严文文-Chris4 小时前
【设计模式-中介者模式】
设计模式·中介者模式
刷帅耍帅4 小时前
设计模式-中介者模式
设计模式·中介者模式
刷帅耍帅5 小时前
设计模式-组合模式
设计模式·组合模式
刷帅耍帅6 小时前
设计模式-命令模式
设计模式·命令模式
码龄3年 审核中6 小时前
设计模式、系统设计 record part03
设计模式
刷帅耍帅6 小时前
设计模式-外观模式
设计模式·外观模式
刷帅耍帅7 小时前
设计模式-迭代器模式
设计模式·迭代器模式