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

Java 線程安全單例模式全集:七種實現方式深度對比

     編輯:Java 綜合教學

單例模式核心契約

  1. 全局唯一實例
  2. 自我實例化
  3. 提供全局訪問點

典型應用:線程池、快取、日誌、對話框、列印機、驅動程式——資源管理器角色,避免狀態不一致與重複佔用。


方式一:餓漢式(靜態常量)

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() { return INSTANCE; }
}
  • 類載入時即創建,天然線程安全(JVM 保證類初始化原子性)
  • 缺點:無法延遲載入,若實例從未使用浪費記憶體

方式二:懶漢式(雙重檢查鎖 DCL)

public class Singleton {
    private static volatile Singleton instance;  // volatile 禁止指令重排
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                    // 第一次檢查(無鎖,效能)
            synchronized (Singleton.class) {       // 類級別鎖
                if (instance == null) {            // 第二次檢查(鎖內)
                    instance = new Singleton();    // 非原子操作:分配記憶體→初始化→賦值
                }
            }
        }
        return instance;
    }
}
  • volatile 關鍵:防止「分配記憶體→賦值引用→初始化」指令重排導致其他線程看到半初始化物件
  • DCL 是高併發下效能與安全平衡的經典方案

方式三:靜態內部類(推薦生產首選)

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、代碼簡潔
  • Bill Pugh 單例模式,Effective Java 推薦

方式四:枚舉單例(防反射、防序列化破壞)

public enum Singleton {
    INSTANCE;
    
    private Connection conn;
    
    Singleton() {  // 建構子自動執行
        // 初始化資源(如資料庫連線)
    }
    
    public Connection getConnection() { return conn; }
}

// 使用
Singleton.INSTANCE.getConnection();
  • JVM 保證枚舉實例全局唯一,天然防反射攻擊(Constructor.newInstance() 拋異常)
  • 序列化機制特殊處理,防序列化破壞單例
  • 代碼最簡潔,功能最完備

封裝枚舉符合單一職責

public class ConnectionFactory {
    private enum Singleton { INSTANCE; ... }
    
    public static Connection getConnection() {
        return Singleton.INSTANCE.getConnection();
    }
}

方式五:靜態代碼塊(餓漢式變體)

public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    static {  // 類載入時執行,線程安全
        instance = new Singleton();
    }
    
    public static Singleton getInstance() { return instance; }
}
  • 可在靜態塊中加入複雜初始化邏輯(try-catch、配置讀取)
  • 本質同方式一,屬餓漢式

方式六:序列化與反序列化保護

問題:預設反序列化會創建新實例,破壞單例。

public class Singleton implements Serializable {
    private static final long serialVersionUID = 1L;
    private static class Holder { static final Singleton INSTANCE = new Singleton(); }
    private Singleton() {}
    public static Singleton getInstance() { return Holder.INSTANCE; }
    
    // 關鍵:反序列化時返回唯一實例
    protected Object readResolve() {
        return Holder.INSTANCE;
    }
}
  • readResolve():反序列化時自動調用,返回規定實例
  • 配合靜態內部類,完美解決序列化破壞單例

七種方式對比總結

方式線程安全延遲載入防反射防序列化破壞複雜度推薦場景
餓漢式(常量)✅❌❌❌低輕量級、必用
DCL✅✅❌❌中高併發、需延遲
靜態內部類✅✅❌❌1低通用首選
枚舉✅✅✅✅極低最強健
靜態代碼塊✅❌❌❌低複雜初始化
1 可加 readResolve() 達到防序列化破壞

實戰建議

  1. 一般業務:靜態內部類(簡潔、安全、延遲)
  2. 資源管理器(連線池、執行緒池):枚舉(防破壞最嚴格)
  3. 需複雜初始化邏輯:靜態代碼塊 + 靜態內部類結合
  4. 避免:單純 synchronized 方法鎖(效能差)、單純同步代碼塊鎖類(同樣低效)、無 volatile 的 DCL(不安全)

單例模式看似簡單,實則蘊含 Java 記憶體模型、類載入機制、反射、序列化等核心知識。選對方式,才能在高併發生產環境穩如磐石。

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