模式概述
單例模式(Singleton Pattern)是設計模式中最基礎但應用最廣泛的創建型模式之一,確保一個類僅有一個實例,并提供一個全局訪問點。這種模式在需要全局唯一對象的場景中至關重要,如配置管理、線程池、數據庫連接池等。
簡單代碼示例(五種經典實現方式對比)
- 餓漢式(線程安全)
特點:線程安全但可能造成資源浪費
public class EagerSingleton {// 類加載時立即初始化(占用內存)private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() {} // 私有構造public static EagerSingleton getInstance() {return instance;}
}
- 懶漢式(線程不安全)
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton(); // 多線程下可能創建多個實例}return instance;}
}
- 同步方法懶漢式(線程安全)
public class SynchronizedSingleton {private static SynchronizedSingleton instance;private SynchronizedSingleton() {}// 方法同步降低性能public static synchronized SynchronizedSingleton getInstance() {if (instance == null) {instance = new SynchronizedSingleton();}return instance;}
}
- 雙重檢查鎖定(DCL)
關鍵點:volatile防止指令重排序,避免返回未初始化完成的對象
public class DoubleCheckedSingleton {// volatile保證可見性和有序性private volatile static DoubleCheckedSingleton instance;private DoubleCheckedSingleton() {}public static DoubleCheckedSingleton getInstance() {if (instance == null) { // 第一次檢查synchronized (DoubleCheckedSingleton.class) {if (instance == null) { // 第二次檢查instance = new DoubleCheckedSingleton();}}}return instance;}
}
- 靜態內部類
public class StaticNestedSingleton {private StaticNestedSingleton() {}// 靜態內部類在首次使用時加載private static class SingletonHolder {private static final StaticNestedSingleton INSTANCE = new StaticNestedSingleton();}public static StaticNestedSingleton getInstance() {return SingletonHolder.INSTANCE; // 由JVM保證線程安全}
}
- 枚舉實現(防序列化破壞)
《Effective Java》推薦方案:無償提供序列化機制,防止反射攻擊
public enum EnumSingleton {INSTANCE; // 枚舉常量本身就是單例public void doSomething() {System.out.println("Singleton working");}
}
// 使用方式:EnumSingleton.INSTANCE.doSomething();
技術細節
1.模式組成
組件 | 職責說明 |
---|---|
Singleton | 單例 |
2.優缺點
優點
- 資源高效利用,減少內存開銷:避免重復創建相同對象
- 全局訪問點,統一入口管理共享資源
- 性能優化,減少對象創建開銷(尤其重量級對象)
缺點
- 違反單一職責原則
- 測試困難,難以模擬依賴
- 擴展性差,難以通過繼承擴展功能
模式應用
Springboot中默認實例化的對象是單例,如果想聲明成多例對象可以使用@Scope(“prototype”)