单例模式确保一个类仅有一个实例,并提供全局访问点。以下是6种经典实现,每种都有特定适用场景,直接上代码+场景分析:
1. 饿汉式(立即加载)
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {} // 私有构造
public static EagerSingleton getInstance() {
return INSTANCE;
}
}
场景:
- 初始化成本低
- 实例必须提前创建(如配置加载)
- 资源敏感场景(可能浪费内存)
2. 懒汉式(线程不安全)
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton(); // 多线程下可能重复创建
}
return instance;
}
}
场景:
- 单线程环境(如工具类)
- 禁止用于多线程
3. 同步方法懒汉式(线程安全)
public class SynchronizedSingleton {
private static SynchronizedSingleton instance;
private SynchronizedSingleton() {}
public static synchronized SynchronizedSingleton getInstance() {
if (instance == null) {
instance = new SynchronizedSingleton();
}
return instance;
}
}
场景:
- 简单多线程场景
- 高并发性能瓶颈(每次调用都加锁)
4. 双重检查锁(DCL)
public class DCLSingleton {
private volatile static DCLSingleton instance; // volatile禁止指令重排序
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
场景:
- 高并发场景(减少锁竞争)
- JDK 5+环境(依赖volatile语义)
5. 静态内部类(推荐)
public class InnerClassSingleton {
private InnerClassSingleton() {}
private static class Holder {
static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return Holder.INSTANCE; // 首次调用时加载内部类
}
}
场景:
- 延迟加载 + 线程安全
- 主流方案(无锁、代码简洁)
6. 枚举单例(最安全)
public enum EnumSingleton {
INSTANCE; // 天生单例
public void doWork() {
System.out.println("Working...");
}
}
// 调用:EnumSingleton.INSTANCE.doWork();
场景:
- 绝对防止反射攻击
- 需要序列化的单例(自动处理序列化)
- 《Effective Java》推荐写法
场景选择速查表
实现方式 | 线程安全 | 延迟加载 | 防反射 | 序列化安全 | 适用场景 |
饿汉式 | 简单应用,启动即用 | ||||
懒汉式(基础) | 单线程工具类 | ||||
同步方法懒汉式 | 低并发多线程 | ||||
双重检查锁 | 高并发性能优化 | ||||
静态内部类 | 主流方案,平衡安全与性能 | ||||
枚举 | 安全要求最高(框架级单例) |
关键结论:
优先选 静态内部类(平衡安全与简洁)
需要绝对安全用 枚举
旧版本JDK高并发用 双重检查锁
避免使用基础懒汉式(线程不安全)
单例陷阱:
- 反射攻击 → 枚举可防御
- 序列化生成新实例 → 重写readResolve()或直接用枚举
- 多类加载器 → 改用上下文类加载器
代码即答案,按场景选用,拒绝过度设计!