首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 單例模式完整指南:餓漢/懶漢、線程安全、反射破壞與防禦

     編輯:Java 相關知識

單例模式核心要素

  1. 私有建構子:防止外部 new。
  2. 私有靜態實例變數:持有唯一實例。
  3. 公有靜態獲取方法:全域存取點。

兩大經典實作

1. 餓漢式(類載入時初始化,天然線程安全)

public class HungrySingleton {
    private static final HungrySingleton INSTANCE = new HungrySingleton();
    private HungrySingleton() {}
    public static HungrySingleton getInstance() { return INSTANCE; }
}

✅ 簡單、無鎖、效能最佳。
⚠️ 類載入即佔用記憶體,若實例極大且未必用到則浪費。

2. 懶漢式(首次呼叫才建立,需手動保證線程安全)

public class LazySingleton {
    private static volatile LazySingleton instance;
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        if (instance == null) {                    // 第一次檢查(無鎖)
            synchronized (LazySingleton.class) {   // 鎖定類物件
                if (instance == null) {            // 第二次檢查(雙重檢查鎖 DCL)
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

✅ 延遲載入、節省資源。
⚠️ 需 volatile 禁止指令重排序,否則 DCL 失效。

測試驗證

Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // true

四大適用場景

  • 頻繁建立/銷毀、建立成本高的物件(資料庫連線池、執行緒池)。
  • 共享資源存取(配置管理器、日誌器)。
  • 有狀態的工具類(ID 生成器、計數器)。
  • 全域唯一控制點(視窗管理器、事件總線)。

⚠️ 三大使用陷阱與防禦

陷阱後果防禦方案
反射呼叫私有建構子產生第二個實例建構子內判斷 if (INSTANCE != null) throw new IllegalStateException()
序列化/反序列化產生新實例實作 readResolve() 回傳現有實例
類載入器隔離不同 ClassLoader 各產生一個指定統一 ClassLoader 或使用枚舉單例

終極推薦:枚舉單例(Effective Java Item 3)

public enum EnumSingleton {
    INSTANCE;
    public void doSomething() { ... }
}

✅ 天然防反射、防序列化、防類載入器隔離、程式碼最簡潔。
⚠️ 不支援延遲載入(類載入即建立),大多數場景可接受。


餓漢式優先、需要延遲載入用 DCL、追求極致防禦用枚舉。切記:單例 ≠ 全域變數,請謹慎評估耦合度與可測試性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved