设计模式之【单例模式】

张开发
2026/4/13 8:44:25 15 分钟阅读

分享文章

设计模式之【单例模式】
单例模式Singleton Pattern单例模式是设计模式中创建型模式的经典代表核心思想是保证一个类在整个应用程序中只有一个实例并提供一个全局访问点来获取这个实例。简单来说单例模式就是让某个类 “独一无二”—— 无论调用多少次创建方法返回的都是同一个对象避免重复创建对象消耗资源比如数据库连接池、配置类、日志工具类等场景。单例模式的实际应用场景工具类比如 java.lang.RuntimeJDK 内置饿汉单例、日志工具类、日期工具类资源密集型对象数据库连接池、Redis 连接池、线程池避免重复创建消耗资源配置类全局配置类应用启动后加载一次配置全局复用Spring 容器Spring 中默认所有 Bean 都是单例通过容器管理唯一实例缓存应用级缓存比如本地缓存工具单例保证缓存数据全局一致。一、核心设计原则私有构造方法禁止外部通过 new 关键字创建实例私有静态实例类内部维护唯一的实例对象公共静态方法提供全局访问点返回这个唯一实例。二、Java 中常见的单例实现方式从易到难从有坑到最优单例模式的实现关键是线程安全和懒加载按需创建实例不同实现方式的核心差异也在这两点。1. 饿汉式饿汉单例—— 最简单线程安全但无懒加载核心逻辑类加载时就创建实例“饿” 意味着迫不及待创建JVM 类加载机制保证线程安全。public class HungrySingleton { // 1. 私有静态实例类加载时直接创建唯一实例 private static final HungrySingleton INSTANCE new HungrySingleton(); // 2. 私有构造方法禁止外部new private HungrySingleton() {} // 3. 公共静态方法返回唯一实例 public static HungrySingleton getInstance() { return INSTANCE; } // 测试方法 public void doSomething() { System.out.println(饿汉单例执行方法实例地址 this); } }特点✅ 线程安全JVM 类加载时仅创建一次实例❌ 无懒加载类加载时就创建即使没用到也会占用内存❌ 无法控制实例创建时机比如需要读取配置后再创建的场景不适用✅ 实现简单无并发问题。2. 懒汉式懒汉单例—— 懒加载但非线程安全基础版核心逻辑第一次调用 getInstance() 时才创建实例“懒” 意味着按需创建但多线程下会创建多个实例。public class LazySingleton { // 1. 私有静态实例初始为null懒加载 private static LazySingleton INSTANCE null; // 2. 私有构造方法 private LazySingleton() {} // 3. 公共静态方法首次调用时创建实例 public static LazySingleton getInstance() { if (INSTANCE null) { // 判空未创建则new INSTANCE new LazySingleton(); } return INSTANCE; } public void doSomething() { System.out.println(懒汉单例执行方法实例地址 this); } }问题多线程场景下多个线程同时进入 if (INSTANCE null)会创建多个实例破坏单例特性。比如// 多线程测试 public class SingletonTest { public static void main(String[] args) { for (int i 0; i 5; i) { new Thread(() - { LazySingleton instance LazySingleton.getInstance(); instance.doSomething(); }).start(); } } }输出会看到多个不同的实例地址说明单例被破坏。3.懒汉式加锁版—— 线程安全但性能差核心逻辑给 getInstance() 加 synchronized 锁保证同一时间只有一个线程执行创建逻辑。public class LazySingletonWithLock { private static LazySingletonWithLock INSTANCE null; private LazySingletonWithLock() {} // 加synchronized锁保证线程安全 public static synchronized LazySingletonWithLock getInstance() { if (INSTANCE null) { INSTANCE new LazySingletonWithLock(); } return INSTANCE; } }特点✅ 线程安全✅ 懒加载❌ 性能差每次调用 getInstance() 都要加锁即使实例已创建。4. 双重检查锁DCL—— 线程安全 懒加载 高性能推荐核心逻辑两次判空 加锁既保证线程安全又避免频繁加锁最优的懒加载单例。public class DclSingleton { // 关键volatile 关键字禁止指令重排必须加 private static volatile DclSingleton INSTANCE null; private DclSingleton() {} public static DclSingleton getInstance() { // 第一次判空实例已创建时直接返回无需加锁提升性能 if (INSTANCE null) { synchronized (DclSingleton.class) { // 类锁只锁创建逻辑 // 第二次判空防止多个线程等待锁后重复创建 if (INSTANCE null) { INSTANCE new DclSingleton(); } } } return INSTANCE; } }关键细节volatile 必须加INSTANCE new DclSingleton() 不是原子操作分为 3 步分配内存2. 初始化对象3. 把 INSTANCE 指向内存地址。不加 volatile 可能发生指令重排步骤 2 和 3 交换导致其他线程拿到 “未初始化完成的实例”。双重判空第一次判空避免频繁加锁第二次判空避免多线程重复创建。特点✅ 线程安全✅ 懒加载✅ 高性能仅首次创建时加锁✅ 实际开发中最常用比如 Spring 中很多单例 Bean 底层用此方式。5. 静态内部类Holder 模式—— 线程安全 懒加载优雅版核心逻辑利用 JVM 静态内部类的加载机制实现懒加载 线程安全无需加锁。public class HolderSingleton { // 私有构造方法 private HolderSingleton() {} // 静态内部类只有调用 getInstance() 时才加载 private static class SingletonHolder { // 内部类加载时创建实例JVM 保证线程安全 private static final HolderSingleton INSTANCE new HolderSingleton(); } // 公共方法返回内部类中的实例 public static HolderSingleton getInstance() { return SingletonHolder.INSTANCE; } }特点✅ 线程安全JVM 加载内部类时仅创建一次实例✅ 懒加载内部类只有被调用时才加载✅ 无锁性能高✅ 代码优雅推荐使用比 DCL 更简洁。6. 枚举单例 —— 绝对线程安全 防反射破坏终极版核心逻辑利用 Java 枚举的特性天然保证单例无法通过反射 / 序列化破坏。public enum EnumSingleton { // 唯一实例枚举常量 INSTANCE; // 业务方法 public void doSomething() { System.out.println(枚举单例执行方法实例地址 this); } } // 直接通过枚举常量获取实例 EnumSingleton instance EnumSingleton.INSTANCE; instance.doSomething();特点✅ 绝对线程安全JVM 保证枚举常量唯一✅ 防止反射破坏反射无法创建枚举实例✅ 防止序列化破坏枚举序列化机制特殊反序列化仍返回同一实例❌ 无懒加载枚举类加载时创建实例✅ 《Effective Java》推荐的最优单例方式无任何坑。特点总结单例模式的核心是保证类只有一个实例 全局访问点核心价值是节省资源、保证状态一致实际开发中优先选 双重检查锁DCL 或 静态内部类懒加载 高性能追求绝对安全选 枚举单例避免使用基础版懒汉式线程不安全和加锁懒汉式性能差单例模式的坑主要在线程安全和指令重排DCL 中 volatile 必须加枚举单例可规避所有坑。

更多文章