0112_单例模式

单例模式 (Singleton Pattern)

意图: 确保一个类只有一个实例,并提供一个全局访问点。
UML图:
Singleton
下面对饿汉式单例懒汉式单例懒汉式单例-双重检查锁静态内部类式枚举单例注册式单例进行说明。

1. 饿汉式单例 (Eager Singleton)

实例在类加载的初始化阶段就已经创建好了。JVM的类加载机制本身保证了线程安全。

public class EagerSingleton {
    // 1. 静态私有成员,在类加载时即完成初始化
    private static final EagerSingleton INSTANCE = new EagerSingleton();

    // 2. 私有构造函数,防止外部new
    private EagerSingleton() {}

    // 3. 公共的静态方法,提供全局访问点
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}
  • 优点:
    • 实现简单,代码易懂。
    • 线程安全:由JVM在类加载时保证初始化一次,绝对安全。
  • 缺点:
    • 可能造成资源浪费:如果这个实例非常庞大,且在程序运行过程中从未被使用过,就是一种浪费。
    • 无法传递参数:在实例化时无法通过getInstance()方法传递初始化参数。

2. 懒汉式单例 (Lazy Singleton) - 基础线程不安全版

只有在调用getInstance()方法时才会创建实例。

public class UnsafeLazySingleton {
    private static UnsafeLazySingleton instance;

    private UnsafeLazySingleton() {}

    public static UnsafeLazySingleton getInstance() {
        // 如果instance为null,则创建新的实例
        if (instance == null) {
            instance = new UnsafeLazySingleton();
        }
        return instance;
    }
}
  • 优点:
    • 延迟加载,避免了资源浪费。
  • 缺点:
    • 线程不安全:在多线程环境下,如果多个线程同时进入if (instance == null)判断,可能会创建出多个实例。

3. 懒汉式单例 (Lazy Singleton) - 双重检查锁 (Double-Checked Locking, DCL)

为了解决基础懒汉式的线程安全问题,并减少同步带来的性能开销,双重检查锁模式应运而生。

public class DclSingleton {
    // 关键:使用volatile关键字禁止指令重排序
    private static volatile DclSingleton instance;

    private DclSingleton() {}

    public static DclSingleton getInstance() {
        // 第一次检查:避免不必要的同步
        if (instance == null) {
            synchronized (DclSingleton.class) {
                // 第二次检查:确保线程进入同步块后实例仍未被创建
                if (instance == null) {
                    instance = new DclSingleton(); // volatile保证了此操作的原子性和可见性
                }
            }
        }
        return instance;
    }
}
  • 优点:
    • 延迟加载,资源利用率高。
    • 线程安全
    • 性能较高:只有在实例未创建时才进行同步,之后调用无需进入同步块。
  • 缺点:
    • 实现稍复杂
    • 需要理解volatile关键字和指令重排序的概念,早期JDK版本中实现有缺陷,现代JDK中已修复。

4. 静态内部类式 (Holder Class) - 推荐的懒汉式实现

这是一种更优雅的实现方式,利用了JVM的类加载机制来保证线程安全,同时实现了延迟加载。

public class HolderSingleton {
    private HolderSingleton() {}

    // 静态内部类持有单例实例
    private static class InstanceHolder {
        private static final HolderSingleton INSTANCE = new HolderSingleton();
    }

    public static HolderSingleton getInstance() {
        // 首次调用getInstance方法才会加载InstanceHolder类并初始化INSTANCE
        return InstanceHolder.INSTANCE;
    }
}
  • 优点:
    • 延迟加载:只有在调用getInstance()时,JVM才会加载InstanceHolder类并初始化INSTANCE
    • 线程安全:由JVM保证类加载过程的线程安全。
    • 实现简单,无需synchronizedvolatile,性能高。
  • 缺点:
    • 无法传递参数。

5. 枚举单例 (Enum Singleton) - 《Effective Java》推荐方式

public enum EnumSingleton {
    INSTANCE; // 唯一的实例

    // 可以添加任意方法
    public void doSomething() {
        System.out.println("Doing something with " + this);
    }
}
// 使用:EnumSingleton.INSTANCE.doSomething();
  • 优点:
    • 绝对线程安全防止反序列化创建新实例
    • 防止反射攻击:JDK内部机制保证枚举类无法被反射实例化。
    • 实现极其简单
  • 缺点:
    • 不是懒加载(枚举实例在第一次被访问时初始化,可视为另一种“懒加载”)。
    • 不够灵活(例如无法继承其他类)。

6. 注册式单例

这通常指的是将单例管理到一个容器(注册表)中,例如使用Map来缓存多种类型的单例对象。Spring框架中的IoC容器就是这种模式的极致体现。

public class SingletonRegistry {
    private static final Map<String, Object> INSTANCES = new ConcurrentHashMap<>();

    private SingletonRegistry() {}

    public static Object getInstance(String className) {
        synchronized (INSTANCES) {
            if (!INSTANCES.containsKey(className)) {
                try {
                    // 根据类名创建实例并放入Map
                    INSTANCES.put(className, Class.forName(className).newInstance());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return INSTANCES.get(className);
        }
    }
}
  • 优点:
    • 管理多种单例,非常灵活。
    • 符合“开闭原则”,易于扩展。
  • 缺点:
    • 需要处理并发问题,以实现线程安全。
    • 实现相对复杂。
    • 从容器中获取实例不如直接调用静态方法直观。

总结对比

模式 线程安全 延迟加载 防止反射/反序列化 实现难度 推荐度
饿汉式 简单 ⭐⭐⭐
懒汉式(不安全) 简单 不推荐
双重检查锁 中等 ⭐⭐⭐⭐
静态内部类 简单 ⭐⭐⭐⭐⭐
枚举 非常简单 ⭐⭐⭐⭐⭐
注册式 需实现 需实现 复杂 ⭐⭐

建议:

  • 如果需要延迟加载,优先选择静态内部类实现。
  • 如果不需要延迟加载,或者实例很小,可以选择饿汉式
  • 如果需要绝对的安全(防止反射和反序列化破坏单例),必须选择枚举实现。
  • 双重检查锁在需要传递初始化参数时仍有其用武之地。
posted @ 2025-08-27 15:49  庞去广  阅读(13)  评论(0)    收藏  举报