單例模式確保系統中僅存在一個類實例,節省資源並便於狀態維護。本文按演進順序剖析 7 種實現方式的優缺點、線程安全性與適用場景,助你精準選型。
public class Singleton {
private Singleton() {}
private static final Singleton INSTANCE = new Singleton();
public static Singleton getInstance() { return INSTANCE; }
}
getInstance() 也會創建對象;構造函數耗時會拖慢類加載。private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) instance = new Singleton();
return instance;
}
synchronized 鎖住整個方法,高併發下效能極差(實測 5 線程 390ms vs 餓漢 0ms)。private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) instance = new Singleton();
}
}
return instance;
}
volatile 禁止指令重排(先分配內存再初始化導致半初始化對象被可見)。public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() { return Holder.INSTANCE; }
}
getInstance() 時才觸發 Holder 初始化。volatile、無同步塊,效能最優。public enum Singleton {
INSTANCE;
public void doSomething() { ... }
}
將類註冊為 @Component / @Bean,容器保證單例作用域。實際企業開發中**最常用**、最無感的方式。
| 場景 | 推薦寫法 | 理由 |
|---|---|---|
| 一般業務、服務端、無特殊延遲需求 | 餓漢式 / 容器管理 | 簡單、無鎖、效能最好 |
| 構造函數極其耗時、或可能永不使用 | 靜態內部類 | 延遲加載 + 零同步開銷 |
| 需防反射/序列化破壞、追求極致簡潔 | 枚舉 | JVM 級保障 |
| 老舊 JDK (≤1.4) 環境 | 同步方法 / DCL (無 volatile) | 歷史妥協 |