单例模式

导读

单例模式(Singleton Pattern)是一种创建型设计模式 ,它确保一个类在整个应用程序生命周期中只有一个实例存在,并提供一个全局访问点来获取该实例。

在 Java 中,常见的实现方式主要有两种:饿汉式(Eager Initialization) 和 懒汉式(Lazy Initialization)。它们各有优缺点,适用于不同场景。

实现方式 是否延迟加载 是否线程安全 是否推荐
饿汉式 ❌ 否 ✅ 是 ✅ 推荐(简单场景)
懒汉式(无锁) ✅ 是 ❌ 否 ❌ 不推荐
懒汉式(synchronized) ✅ 是 ✅ 是 ⚠️ 可接受(性能略差)
双重检查锁定 ✅ 是 ✅ 是 ✅ 推荐
静态内部类 ✅ 是 ✅ 是 ✅ 推荐
枚举 ✅ 是 ✅ 是 ✅✅ 最推荐

🎯 应用场景

  • 数据库连接池管理
  • 日志记录器(如 Logger)
  • 配置管理类(如 Properties)
  • Spring 中的 Bean 默认就是单例模式

一、饿汉式(Eager Initialization)

特点 :类加载时立即创建实例(急切初始化)
优点 :线程安全,无需加锁(由 JVM 类加载机制保证)
缺点:可能造成资源浪费(未使用时也占用内存)

1. 基础实现(静态变量)

java 复制代码
public class EagerSingleton {
    // 类加载时初始化实例
    private static final EagerSingleton INSTANCE = new EagerSingleton();
    
    private EagerSingleton() {}  // 私有构造器
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

2. 静态代码块变体

java 复制代码
public class EagerSingleton {
    private static final EagerSingleton INSTANCE;
    
    static {
        try {
            INSTANCE = new EagerSingleton();
        } catch (Exception e) {
            throw new RuntimeException("初始化失败", e);
        }
    }
    
    public static EagerSingleton getInstance() {
        return INSTANCE;
    }
}

3. 枚举实现(终极方案)

java 复制代码
public enum EnumSingleton {
    INSTANCE;  // 单例实例
    
    public void doSomething() {
        // 业务方法
    }
}
// 使用:EnumSingleton.INSTANCE.doSomething();

优势:

  • 绝对防止反射攻击
  • 自动处理序列化/反序列化
  • 线程安全
  • 代码最简洁(Java 官方推荐方式)

二、懒汉式(Lazy Initialization)

特点 :首次使用时创建实例(延迟初始化)
优点 :节省资源
缺点:需额外处理线程安全问题

1. 非线程安全基础版

java 复制代码
public class UnsafeLazySingleton {
    private static UnsafeLazySingleton instance;
    
    private UnsafeLazySingleton() {}
    
    public static UnsafeLazySingleton getInstance() {
        if (instance == null) {  // 线程不安全点
            instance = new UnsafeLazySingleton();
        }
        return instance;
    }
}

2. 线程安全同步方法版(低效)

java 复制代码
public class SynchronizedLazySingleton {
    private static SynchronizedLazySingleton instance;
    
    private SynchronizedLazySingleton() {}
    
    public static synchronized SynchronizedLazySingleton getInstance() {
        if (instance == null) {
            instance = new SynchronizedLazySingleton();
        }
        return instance;
    }
}

缺点:每次获取实例都同步,性能差

3. 双重检查锁定(DCL)版(高效线程安全)

java 复制代码
public class DoubleCheckedLockingSingleton {
    // volatile 禁止指令重排序
    private static volatile DoubleCheckedLockingSingleton instance;
    
    private DoubleCheckedLockingSingleton() {}
    
    public static DoubleCheckedLockingSingleton getInstance() {
        if (instance == null) {  // 第一次检查(无锁)
            synchronized (DoubleCheckedLockingSingleton.class) {
                if (instance == null) {  // 第二次检查(有锁)
                    instance = new DoubleCheckedLockingSingleton();
                }
            }
        }
        return instance;
    }
}

关键点:

  • volatile 防止 JVM 指令重排序(避免返回未初始化的对象)
  • 两次判空确保单例

4. 静态内部类(Holder)模式(推荐)

java 复制代码
public class HolderSingleton {
    private HolderSingleton() {}
    
    private static class Holder {
        // 由 JVM 保证线程安全初始化
        static final HolderSingleton INSTANCE = new HolderSingleton();
    }
    
    public static HolderSingleton getInstance() {
        return Holder.INSTANCE;  // 首次调用时加载 Holder 类
    }
}

优势:

  • 线程安全(JVM 类加载机制)
  • 懒加载(调用 getInstance() 时才初始化)
  • 无同步开销

五、选型建议

  1. 首选枚举方案(Java 5+)

    • 满足所有安全需求
    • 代码简洁明了(《Effective Java》推荐)
  2. 需要懒加载时

    • 选静态内部类(平衡安全性与性能)
    • 避免使用基础同步方法(性能差)
  3. 明确需要饿加载

    • 简单场景用饿汉式
    • 需异常处理时用静态块变体
  4. 超高性能场景

    • 首选枚举 或饿汉式
    • 次选双重检查锁定(注意 volatile)

📌 设计原则:在满足需求的前提下,选择最简单的实现。单例模式已被认为是一种"反模式",过度使用会导致代码耦合度高、难以测试,建议结合依赖注入框架(如Spring)管理单例。

相关推荐
脚本自动化开发3 小时前
折叠双屏MU156LA:写稿修图游戏体验
后端
脚本自动化开发3 小时前
AI短剧800集讲规矩,观众忘了是AI
后端
Csvn5 小时前
安装、升级与目录结构
后端·nginx
皮皮林5515 小时前
万能通用!权限系统就该这么设计
后端
GoAI6 小时前
DeepSeek Harness Memory (一)主流记忆插件总结
后端·算法·面试
橙子家7 小时前
RESTful 接口:它并非标准或协议,而是一种【架构风格】
后端
小兔子8 小时前
dict.get 的默认值每次都会求值,哪怕键本来就在
后端
斯维赤8 小时前
限流、熔断、降级,你真的分得清吗?
java·后端·架构
肌肉娃子8 小时前
红队攻防测试报告 — wechat-e.cfd
后端
陈随易8 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员