Java 單例模式實現方式
??單例模式是確保一個類只有一個實例,并提供一個全局訪問點的設計模式。以下是 Java 中實現單例模式的幾種常見方式:
1. 餓漢式(Eager Initialization)
public class EagerSingleton {// 類加載時就初始化private static final EagerSingleton INSTANCE = new EagerSingleton();// 私有構造函數private EagerSingleton() {}public static EagerSingleton getInstance() {return INSTANCE;}
}
特點:
- 線程安全(由JVM類加載機制保證)
- 簡單直接
- 可能造成資源浪費(即使不用也會創建實例)
2. 懶漢式(Lazy Initialization,非線程安全)
public class UnsafeLazySingleton {private static UnsafeLazySingleton instance;private UnsafeLazySingleton() {}public static UnsafeLazySingleton getInstance() {if (instance == null) {instance = new UnsafeLazySingleton();}return instance;}
}
問題:
- 非線程安全,多線程環境下可能創建多個實例
3. 懶漢式(同步方法,線程安全但效率低)
public class SynchronizedLazySingleton {private static SynchronizedLazySingleton instance;private SynchronizedLazySingleton() {}public static synchronized SynchronizedLazySingleton getInstance() {if (instance == null) {instance = new SynchronizedLazySingleton();}return instance;}
}
特點:
- 線程安全
- 每次獲取實例都需要同步,性能較差
4. 雙重檢查鎖定(Double-Checked Locking)
public class DCLSingleton {// 使用volatile禁止指令重排序private static volatile DCLSingleton instance;private DCLSingleton() {}public static DCLSingleton getInstance() {if (instance == null) { // 第一次檢查synchronized (DCLSingleton.class) {if (instance == null) { // 第二次檢查instance = new DCLSingleton();}}}return instance;}
}
特點:
- 線程安全
- 只有第一次創建時需要同步
- Java 5+ 需要配合 volatile 使用
5. 靜態內部類(Holder模式,推薦)
public class HolderSingleton {private HolderSingleton() {}private static class SingletonHolder {private static final HolderSingleton INSTANCE = new HolderSingleton();}public static HolderSingleton getInstance() {return SingletonHolder.INSTANCE;}
}
優點:
- 線程安全(由JVM類加載機制保證)
- 懶加載(只有調用getInstance()時才加載內部類)
- 無同步開銷
- 目前最推薦的方式
6. 枚舉實現(Effective Java推薦)
public enum EnumSingleton {INSTANCE;public void doSomething() {// 業務方法}
}
優點:
- 線程安全
- 防止反射攻擊
- 防止反序列化重新創建對象
- 代碼簡潔
- Joshua Bloch在《Effective Java》中推薦的方式
如何選擇
- 簡單場景:餓漢式或枚舉方式
- 需要懶加載:靜態內部類方式
- 非常注重性能:雙重檢查鎖定(但要注意正確實現)
- 最佳實踐推薦:枚舉方式或靜態內部類方式
注意事項
- 防止反射攻擊:可以在構造函數中添加檢查
- 防止反序列化:實現
readResolve()
方法 - 在分布式/集群環境中,這些單例實現僅適用于單個JVM