写出三种单例模式实现¶
一、饿汉式¶
类加载时就初始化,线程安全:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return INSTANCE; }
}
- ✅ 简单、线程安全。
- ❌ 类加载就创建,不懒加载。
二、双重检查锁(DCL)¶
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次检查
synchronized (Singleton.class) {
if (instance == null) { // 第二次检查
instance = new Singleton();
}
}
}
return instance;
}
}
- ✅ 懒加载、线程安全、高性能。
- ❌ 必须用
volatile,防止指令重排。
为什么要 volatile¶
new Singleton() 分三步:
1. 分配内存。
2. 初始化对象。
3. 引用指向内存。
JIT 可能重排成 1-3-2,另一个线程看到 instance 非 null 但对象还没初始化。
三、静态内部类¶
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
- ✅ 懒加载(Holder 类第一次用才加载)。
- ✅ 线程安全(JVM 保证)。
- ✅ 不需要 volatile。
推荐写法。
四、枚举(最推荐)¶
- ✅ 线程安全。
- ✅ 防反射破坏。
- ✅ 防反序列化重建。
Effective Java 推荐。
五、对比¶
| 写法 | 懒加载 | 线程安全 | 防反射 |
|---|---|---|---|
| 饿汉式 | ❌ | ✅ | ❌ |
| DCL | ✅ | ✅ | ❌ |
| 静态内部类 | ✅ | ✅ | ❌ |
| 枚举 | ❌ | ✅ | ✅ |
高频追问
- 反射怎么破坏单例?
getDeclaredConstructor().setAccessible(true).newInstance()。 - 枚举为什么防反射?JDK 规定反射创建枚举会抛异常。