單例模式確保類別在全域僅有一個實例,並提供全域存取點。實現關鍵只有兩點:私有化建構子(防外部 new)與 靜態方法/屬性提供唯一實例。本文詳細比較四種經典寫法並補充雙重檢查鎖(DCL)與靜態內部類兩種進階寫法。
需求時才建立,多執行緒下必須加同步,否則多執行緒同時進入 if (instance == null) 會產生多個實例。
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {}
public static synchronized LazySingleton getInstance() {
if (instance == null) instance = new LazySingleton();
return instance;
}
}
缺點:synchronized 鎖住整個方法,效能差;高併發下成為瓶頸。
類別載入時即建立實例,天然執行緒安全(JVM 類別載入機制保證)。
public class EagerSingleton {
private static final EagerSingleton INSTANCE = new EagerSingleton();
private EagerSingleton() {}
public static EagerSingleton getInstance() { return INSTANCE; }
}
缺點:若實例建立耗時或資源重,且可能不被使用,造成資源浪費。
本質同餓漢,利用 static final 語義保證唯一性。
public class StaticFinalSingleton {
public static final StaticFinalSingleton INSTANCE = new StaticFinalSingleton();
private StaticFinalSingleton() {}
}
結合懶漢延遲載入與執行緒安全,利用 JVM 類別載入機制:外部類別載入時不載入內部類,只有呼叫 getInstance() 時才載入 Holder 並初始化 INSTANCE。
public class InnerClassSingleton {
private InnerClassSingleton() {}
private static class Holder {
static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() { return Holder.INSTANCE; }
}
推薦程度:★★★★★ 兼具延遲載入、高效能、執行緒安全、程式碼簡潔。
試圖兼顧延遲載入與效能,但 JDK 1.5 前因指令重排序有缺陷,需加 volatile 修飾 instance。
public class DCLSingleton {
private volatile static DCLSingleton instance;
private DCLSingleton() {}
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) instance = new DCLSingleton();
}
}
return instance;
}
}
public class SingletonTest {
public static void main(String[] args) {
// 啟動大量執行緒同時呼叫 getInstance()
// 驗證所有執行緒拿到的引用是否指向同一物件
}
}
LoadProperties):全域唯一、避免重複 I/O。public,違背單例初衷。