上一篇
单例模式确保类仅有一个实例,并提供全局访问点,常用实现有饿汉式(线程安全但立即加载)、懒汉式(延迟加载需同步锁)、静态内部类(延迟且线程安全)及枚举(最简洁,防反射序列化破坏)。
饿汉式(线程安全)
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官方文档)。



