寫法一:餓漢式(類加載即初始化)
public class SingletonEager {
private static final SingletonEager INSTANCE = new SingletonEager();
private SingletonEager() {}
public static SingletonEager getInstance() { return INSTANCE; }
}
- ✅ 線程安全(ClassLoader 鎖)
- ✅ 實現最簡單
- ❌ 無延遲加載,啟動即佔用資源
- ❌ 構造拋異常導致類不可用
- ❌ 可被反射破壞:
Constructor.setAccessible(true) 可創建第二個實例
- ❌ 可被序列化破壞:需實現
readResolve() 返回 INSTANCE
寫法二:靜態內部類(延遲加載 + 線程安全 + 簡潔)⭐ 推薦通用首選
public class SingletonHolder {
private SingletonHolder() {}
private static class Holder {
static final SingletonHolder INSTANCE = new SingletonHolder();
}
public static SingletonHolder getInstance() { return Holder.INSTANCE; }
}
- ✅ 延遲加載:調用
getInstance() 觸發 Holder 類初始化
- ✅ 線程安全:JVM 保證類初始化原子性
- ✅ 無鎖、無
volatile、代碼極簡
- ❌ 同樣可被反射/序列化破壞(需額外防禦)
寫法三:雙重檢查鎖(DCL,JDK 5+ 必須加 volatile)
public class SingletonDCL {
private static volatile SingletonDCL instance; // 關鍵:禁止指令重排序
private SingletonDCL() {}
public static SingletonDCL getInstance() {
if (instance == null) { // 第一次檢查:無鎖快速路徑
synchronized (SingletonDCL.class) { // 類級鎖
if (instance == null) { // 第二次檢查:防重複創建
instance = new SingletonDCL();
}
}
}
return instance;
}
}
- ✅ 延遲加載、高併發性能好(僅首次同步)
- ⚠️ volatile 必不可少:防止
instance = new Singleton() 重排序導致半初始化對象可見
- ❌ 代碼較繁瑣,易寫錯(漏 volatile、漏二次檢查、鎖對象錯誤)
- ❌ 同樣怕反射/序列化
寫法四:枚舉單例(Effective Java 作者 Josh Bloch 強推)🛡️ 最強防禦
public enum SingletonEnum {
INSTANCE;
// 可添加字段、方法
public void doSomething() { System.out.println("價值連城"); }
}
- ✅ 天然線程安全(枚舉類初始化由 ClassLoader 保證)
- ✅ 天然防反射:JVM 禁止通過反射調用枚舉構造器
- ✅ 天然防序列化破壞:序列化機制特殊處理,反序列化直接返回枚舉常量
- ✅ 代碼最少,語義最清晰
- ❌ 無延遲加載(枚舉類加載時即創建)
- ❌ 不支持繼承、不支持懶加載邏輯(如配置讀取後再初始化)
防反射/序列化通用加固模板(適用前三種)
public class HardenedSingleton implements Serializable {
private static final long serialVersionUID = 1L;
private static volatile HardenedSingleton instance;
private HardenedSingleton() {
// 防反射:若已存在實例則拋異常
if (instance != null) {
throw new IllegalStateException("單例已存在,禁止反射創建");
}
}
public static HardenedSingleton getInstance() {
if (instance == null) {
synchronized (HardenedSingleton.class) {
if (instance == null) instance = new HardenedSingleton();
}
}
return instance;
}
// 防序列化:反序列化時返回現有實例
protected Object readResolve() { return instance; }
}
對比總結表
| 維度 | 餓漢式 | 靜態內部類 | DCL | 枚舉 |
| 線程安全 | ✅ | ✅ | ✅ (volatile) | ✅ |
| 延遲加載 | ❌ | ✅ | ✅ | ❌ |
| 性能 | 極高 | 極高 | 高 | 極高 |
| 代碼複雜度 | 低 | 低 | 中 | 極低 |
| 防反射 | ❌ | ❌ | ❌ | ✅ |
| 防序列化 | ❌ | ❌ | ❌ | ✅ |
| 推薦場景 | 輕量、必用、啟動即需 | 通用首選 | 需精確控制初始化時機 | 安全要求極高、框架級組件 |
實戰選型建議
- 一般業務單例:靜態內部類,平衡延遲加載、線程安全、代碼簡潔。
- 框架核心組件、安全敏感組件:枚舉單例,零成本防反射/序列化破壞。
- 需動態參數初始化(如讀取配置後才能 new):DCL + volatile 或
AtomicReference + CAS。
- Spring 管理的 Bean:直接用
@Component @Scope("singleton"),容器保證單例、生命周期、AOP 代理一致性,不要手寫單例。