上一篇
Java单例模式如何正确实现?
- 后端开发
- 2025-07-07
- 3441
单例模式确保类仅有一个实例,并提供全局访问点,常用实现有饿汉式(线程安全但立即加载)、懒汉式(延迟加载需同步锁)、静态内部类(延迟且线程安全)及枚举(最简洁,防反射序列化破坏)。
饿汉式(线程安全)
public class Singleton { // 类加载时直接初始化 private static final Singleton INSTANCE = new Singleton(); // 私有构造方法防止外部实例化 private Singleton() {} public static Singleton getInstance() { return INSTANCE; } }
- 优点:线程安全(JVM类加载机制保证)
- 缺点:类加载时即创建实例,可能造成资源浪费
- 适用场景:实例占用资源少且频繁使用
懒汉式(线程不安全版)
public class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { // 线程不安全点 instance = new Singleton(); } return instance; } }
- 风险:多线程下可能创建多个实例
- 仅限单线程环境使用
双重检查锁(DCL,线程安全)
public class Singleton { private static volatile Singleton instance; // volatile禁止指令重排序 private Singleton() {} public static Singleton getInstance() { if (instance == null) { // 第一次检查 synchronized (Singleton.class) { // 加锁 if (instance == null) { // 第二次检查 instance = new Singleton(); } } } return instance; } }
- 关键点:
volatile
防止JVM指令重排序(避免返回未初始化的对象) - 优点:线程安全且延迟加载
- 工业级推荐方案
静态内部类(线程安全)
public class Singleton { private Singleton() {} private static class Holder { static final Singleton INSTANCE = new Singleton(); } public static Singleton getInstance() { return Holder.INSTANCE; // 首次调用时加载内部类 } }
- 原理:利用JVM类加载机制(静态内部类在调用时才加载)
- 优点:无需同步锁,兼顾线程安全和懒加载
枚举(最佳实践)
public enum Singleton { INSTANCE; // 枚举实例天然单例 public void doSomething() { // 业务方法 } } // 调用方式:Singleton.INSTANCE.doSomething();
- 优势:
- 绝对线程安全(JVM保证)
- 防止反射攻击和序列化破坏单例
- 《Effective Java》作者Joshua Bloch推荐方案
破坏单例的场景及防护
威胁 | 防护方案 |
---|---|
反射攻击 | 私有构造器中添加throw new RuntimeException() |
序列化破坏 | 实现readResolve() 方法返回实例 |
// 序列化防护示例 protected Object readResolve() { return getInstance(); }
总结与选择建议
- 简单场景:直接使用饿汉式
- 延迟加载需求:优先选择静态内部类或枚举
- 高并发系统:双重检查锁(DCL)
- 终极方案:枚举(防反射/序列化攻击)
单例模式虽实用,但需注意两点:
- 过度使用会导致代码耦合度高(违背单一职责原则)
- 在分布式系统或类加载器不同的环境中可能失效
引用说明:
双重检查锁实现参考《Java并发编程实战》(Brian Goetz等);枚举方案出自《Effective Java》(Joshua Bloch);类加载机制依据JVM规范(Oracle官方文档)。