單例模式確保某個類別在整個應用程式中只存在一個實例,並提供全域存取點。這是最常用的設計模式之一,本文將詳細分析五種主流實現方式的原理、優缺點與效能表現。
單例模式的核心在於:私有化建構子、提供靜態工廠方法、確保執行緒安全。根據實例建立時機,主要分為「餓漢式」(類別載入時建立)與「懶漢式」(首次使用時建立)兩大類。
類別載入階段即建立實例,利用類別載入機制天然保證執行緒安全。
public class SingletonDemo01 {
private static SingletonDemo01 instance = new SingletonDemo01();
private SingletonDemo01() {}
public static SingletonDemo01 getInstance() {
return instance;
}
}
特點:執行緒安全、呼叫效率高、無法延遲載入、佔用資源較小時適用。
首次呼叫時才建立實例,方法加 synchronized 保證執行緒安全。
public class SingletonDemo02 {
private static SingletonDemo02 instance;
private SingletonDemo02() {}
public static synchronized SingletonDemo02 getInstance() {
if (instance == null) {
instance = new SingletonDemo02();
}
return instance;
}
}
特點:執行緒安全、可延遲載入、但同步導致併發效能低。
嘗試結合延遲載入與高效能,但在舊版 JVM 因指令重排序可能失效,現代建議配合 volatile 關鍵字使用,仍不建議作為首選。
利用類別載入機制實現延遲載入與執行緒安全的完美平衡。
public class SingletonDemo04 {
private SingletonDemo04() {}
private static class SingletonHolder {
private static final SingletonDemo04 INSTANCE = new SingletonDemo04();
}
public static SingletonDemo04 getInstance() {
return SingletonHolder.INSTANCE;
}
}
特點:執行緒安全、呼叫效率高、支援延遲載入、程式碼簡潔。
利用列舉特性天然防止反射攻擊與序列化破壞,效能最佳。
public enum SingletonDemo05 {
INSTANCE;
public void singletonOpt() {}
}
特點:執行緒安全、效能最高、防反射/序列化破壞、無法延遲載入。
| 場景 | 推薦方案 | 備選方案 |
|---|---|---|
| 資源佔用小、無需延遲載入 | 列舉式 | 餓漢式 |
| 資源佔用大、需延遲載入 | 靜態內部類 | 懶漢式 |
避免使用雙重檢查鎖定,除非深度理解 JVM 記憶體模型並配合 volatile 正確實現。
單例模式看似簡單,實則蘊含豐富的並發與類別載入知識。現代 Java 開發中,列舉式與靜態內部類是最佳實踐,前者適合簡單場景,後者適合需延遲初始化的複雜場景。理解各方案優劣,才能在架構設計中做出正確選擇。