單例模式確保類在 JVM 中僅有一個實例,並提供全局訪問點。它是最基礎也最常考的設計模式,但在多線程、序列化、反射攻擊等場景下隱藏諸多陷阱。
new)getInstance())public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return INSTANCE; }
}
優點:線程安全、實現簡單、調用效率高。
缺點:類加載即佔用記憶體,無法延遲加載;若實例化拋出異常會導致類不可用。
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) instance = new Singleton();
return instance;
}
}
缺陷:每次調用都加鎖,性能極差,不推薦生產使用。
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 禁止指令重排,保證初始化可見性。JDK 1.5+ 修復內存模型後才可靠。
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { return Holder.INSTANCE; }
}
優勢:利用類加載機制天然線程安全、延遲加載、無鎖高效、防序列化/反射破壞(需額外處理)。
public enum Singleton {
INSTANCE;
public void doSomething() {}
}
終極防線:JVM 保證單例、免費防序列化破壞、防反射攻擊、代碼最簡潔。
Spring 容器本質即單例註冊表。自定義實現可用 ConcurrentHashMap 存儲 ClassName -> Instance,支援多單例管理、動態註冊銷毀,適合插件化架構。
public class ThreadLocalSingleton {
private static final ThreadLocal<ThreadLocalSingleton> HOLDER =
ThreadLocal.withInitial(ThreadLocalSingleton::new);
private ThreadLocalSingleton() {}
public static ThreadLocalSingleton getInstance() { return HOLDER.get(); }
}
場景:每個線程擁有獨立實例,如數據庫連接、Session 上下文、請求級緩存。
| 攻擊手段 | 防護措施 |
|---|---|
| 反射調用私有構造器 | 構造器中檢查 instance != null 拋出異常 |
| 序列化/反序列化 | 實現 readResolve() 返回同一實例 |
| 跨類加載器 | 指定上下文類加載器、雙親委派模型 |