確保一個類全局僅有一個實例,並提供全局訪問點。適用於:配置管理器、連接池、線程池、日誌記錄器、緩存管理器等。
public class SingletonEager {
private static final SingletonEager INSTANCE = new SingletonEager();
private SingletonEager() {}
public static SingletonEager getInstance() { return INSTANCE; }
}
優點:簡單、無鎖、效能最高。缺點:類加載即創建,若實例極重且未必使用,浪費記憶體。
public class SingletonLazySync {
private static SingletonLazySync instance;
private SingletonLazySync() {}
public static synchronized SingletonLazySync getInstance() {
if (instance == null) instance = new SingletonLazySync();
return instance;
}
}
每次調用加鎖,高併發下嚴重影響吞吐。
public class SingletonDCL {
private static volatile SingletonDCL instance; // 關鍵 volatile
private SingletonDCL() {}
public static SingletonDCL getInstance() {
if (instance == null) { // 第一次檢查(無鎖)
synchronized (SingletonDCL.class) { // 鎖定類對象
if (instance == null) { // 第二次檢查(鎖內)
instance = new SingletonDCL();
}
}
}
return 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 保證線程安全。
public enum SingletonEnum {
INSTANCE;
public void doSomething() { System.out.println("do something"); }
}
優點:語法最簡、天然防序列化重新創建實例、防反射攻擊(構造器私有化)。缺點:不支援延遲加載、無法繼承、無法實現複雜初始化邏輯。
現代框架開發中,交由 IoC 容器管理單例生命週期,配合 @PostConstruct 初始化、@PreDestroy 銷毀,最符合工程實踐。
| 攻擊手段 | 受影響實現 | 防禦 |
|---|---|---|
| 反射調用私有構造器 | 除枚舉外所有 | 構造器中檢查 if (instance != null) throw new IllegalStateException() |
| 序列化/反序列化 | 實現 Serializable 的單例 | 實現 readResolve() 返回現有實例;或用枚舉 |
| 多類加載器 | 所有靜態字段單例 | 指定統一類加載器、或使用容器管理 |