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

Java 單例模式深度解析:六種實現、破壞場景與線程安全實戰

     編輯:Java 綜合教學

單例模式核心價值與權衡

單例模式確保 JVM 進程中僅存在一個實例,帶來雙重收益:

  • 性能層面:減少重量級對象創建開銷,降低 GC 壓力,縮短停頓時間
  • 設計層面:邏輯上全局唯一(如配置管理器、連接池),保證狀態一致性

代價:無抽象層難擴展、職責過重違背單一職責、濫用導致資源爭用、序列化/反射/類加載器可能破壞單一性。

六大經典實現方式

1. 餓漢式(類加載時初始化)

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

✅ 線程安全、調用快 ❌ 無延遲加載、類加載即佔用內存

2. 懶漢式(首次調用初始化)

public class Singleton2 {
    private Singleton2() {}
    private static Singleton2 instance;
    public static Singleton2 getInstance() {
        if (instance == null) instance = new Singleton2(); // 非線程安全
        return instance;
    }
}

✅ 延遲加載 ❌ 多線程下會創建多實例

3. 雙重檢查鎖 DCL(JDK 1.5+ volatile 修復指令重排)

public class Singleton3 {
    private Singleton3() {}
    private static volatile Singleton3 instance;
    public static Singleton3 getInstance() {
        if (instance == null) {
            synchronized (Singleton3.class) {
                if (instance == null) instance = new Singleton3();
            }
        }
        return instance;
    }
}

✅ 延遲+線程安全+高性能 ❌ 代碼較繁瑣、首次調用稍慢

4. 靜態內部類(推薦:延遲+線程安全+簡潔)

public class Singleton4 {
    private Singleton4() {}
    private static class Holder {
        static final Singleton4 INSTANCE = new Singleton4();
    }
    public static Singleton4 getInstance() { return Holder.INSTANCE; }
}

利用類加載機制天然線程安全,Holder 類僅在首次調用 getInstance() 時加載,完美平衡所有指標。

5. 枚舉單例(Effective Java 作者推薦)

public enum Singleton5 {
    INSTANCE;
    public void doSomething() { /* 业务逻辑 */ }
}

✅ 防反射、防序列化破壞、語法最簡 ❌ 無延遲加載、不支持懶加載邏輯

6. 容器管理單例(Spring 模式)

public class SingletonManager {
    private static final Map CONTAINER = new ConcurrentHashMap<>();
    public static void register(String key, Object instance) {
        CONTAINER.putIfAbsent(key, instance);
    }
    public static Object get(String key) { return CONTAINER.get(key); }
}

適用於管理多類型單例,統一獲取入口,降低耦合,框架級應用首選。

三大破壞單例的場景與對策

場景原理對策
反射調用私有構造器Constructor.setAccessible(true) 強行實例化構造器中檢查 if (INSTANCE != null) throw Exception 或改用枚舉
多類加載器不同 ClassLoader 加載同一類產生多個 Class 對象指定統一類加載器(如線程上下文類加載器)
序列化/反序列化反序列化默認創建新對象實現 readResolve() 返回唯一實例

序列化破壞演示與修復

// 破壞前
ObjectInputStream.readObject() → newSingleton != singleton (false)

// 修復:添加 readResolve()
private Object readResolve() { return INSTANCE; }

// 修復後
singleton == newSingleton (true)

選型決策樹

  1. 需防反射/序列化破壞 → 枚舉
  2. 需延遲加載、極致簡潔 → 靜態內部類
  3. 高並發、追求極致性能 → DCL / 靜態內部類
  4. 框架級、管理多類型單例 → 容器模式
  5. 簡單工具類、確定會用 → 餓漢式

生產環境最佳實踐

  • 優先考慮 依賴注入框架(Spring @Singleton) 管理生命周期
  • 業務單例實現 Serializable + readResolve()
  • 避免在單例中持有非線程安全狀態,或使用 ThreadLocal 隔離
  • 單例職責單一化,拒絕「上帝類」
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved