單例模式是最常用的創(chuàng)建型設計模式之一,核心目標是保證一個類在整個應用生命周期內(nèi)只有一個實例,并提供全局唯一的訪問入口。本文梳理了單例模式的 5 種經(jīng)典實現(xiàn)方式,分析各方案的優(yōu)缺點及適用場景,同時給出工業(yè)級最佳實踐。
一、懶漢式單例模式(加鎖版,基礎版不加鎖,線程不安全)
核心特點:延遲初始化(第一次調(diào)用時創(chuàng)建實例),通過方法同步保證線程安全,但性能損耗大。
public class Singleton {
// 私有靜態(tài)實例,初始為null(延遲加載)
private static Singleton instance;
// 私有構(gòu)造方法:禁止外部new創(chuàng)建實例
private Singleton() {}
// 同步方法:保證多線程下唯一實例
public static synchronized Singleton getInstance() {
if (instance == null) { // 懶加載:僅在首次調(diào)用時創(chuàng)建
instance = new Singleton();
}
return instance;
}
}
優(yōu)缺點
- ? 優(yōu)點:完全延遲加載,無資源浪費;線程安全;實現(xiàn)簡單。
- ? 缺點:synchronized 加在方法上,每次調(diào)用 getInstance() 都會加鎖,即使實例已創(chuàng)建,高并發(fā)場景下性能差。
適用場景
并發(fā)量極低、對性能要求不高的簡單場景(幾乎不推薦在生產(chǎn)環(huán)境使用)。
二、餓漢式單例模式
核心特點:類加載時立即初始化實例,天然線程安全,但可能造成資源浪費。
public class Singleton {
// 類加載階段(初始化階段)就創(chuàng)建實例,JVM保證線程安全
private static final Singleton instance = new Singleton();
// 私有構(gòu)造方法
private Singleton() {}
// 無鎖獲取實例,性能極高
public static Singleton getInstance() {
return instance;
}
}
優(yōu)缺點
- ? 優(yōu)點:線程安全(JVM 類加載機制保證);無鎖開銷,性能最優(yōu);實現(xiàn)簡單。
- ? 缺點:非延遲加載,若實例創(chuàng)建依賴重資源(如數(shù)據(jù)庫連接、大對象),且長期未使用,會造成資源浪費。
適用場景
實例創(chuàng)建成本低、啟動后大概率會被使用的場景(如工具類、輕量級配置類)。
三、雙重檢查鎖(DCL)單例模式
核心特點:兼顧延遲加載和線程安全,僅在實例未創(chuàng)建時加鎖,性能接近餓漢式。需通過 volatile 解決指令重排問題。
public class Singleton {
// volatile關鍵字:1.保證可見性 2.禁止指令重排(關鍵?。? private static volatile Singleton instance;
// 私有構(gòu)造方法
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { // 第一次檢查:實例已創(chuàng)建則直接返回,避免加鎖
synchronized (Singleton.class) { // 類對象鎖:僅當實例未創(chuàng)建時加鎖
if (instance == null) { // 第二次檢查:防止多線程并發(fā)創(chuàng)建
instance = new Singleton();
}
}
}
return instance;
}
}
關鍵解釋
instance = new Singleton() 并非原子操作,JVM 會拆分為 3 步:
1、分配內(nèi)存空間;
2、初始化實例;
3、將 instance 指向內(nèi)存地址。
若不加 volatile,JVM 可能指令重排為 1→3→2,導致其他線程在第二步完成前,看到 instance 不為 null 但實例未初始化,引發(fā)空指針異常。
優(yōu)缺點
- ? 優(yōu)點:延遲加載;僅首次創(chuàng)建時加鎖,高并發(fā)性能優(yōu)異;線程安全。
- ? 缺點:實現(xiàn)稍復雜,易因遺漏 volatile 導致線程安全問題。
適用場景
高并發(fā)場景、實例創(chuàng)建成本高且需延遲加載的場景(生產(chǎn)環(huán)境常用)。
四、靜態(tài)內(nèi)部類單例模式(推薦)
核心特點:利用 JVM 類加載機制實現(xiàn)延遲加載 + 線程安全,兼顧性能與優(yōu)雅性,是最推薦的非枚舉實現(xiàn)方式。
public class Singleton {
// 私有構(gòu)造方法
private Singleton() {}
// 靜態(tài)內(nèi)部類:獨立于外部類,僅在被調(diào)用時加載
private static class InnerClass {
// JVM保證靜態(tài)變量初始化的線程安全
private static final Singleton instance = new Singleton();
}
// 外部類調(diào)用時,才觸發(fā)內(nèi)部類加載和實例創(chuàng)建
public static Singleton getInstance() {
return InnerClass.instance;
}
}
關鍵解釋
- 外部類 Singleton 加載時,靜態(tài)內(nèi)部類 InnerClass 不會被加載,實現(xiàn)延遲加載;
- 調(diào)用 getInstance() 時,InnerClass 被 JVM 加載,其靜態(tài)變量 instance 由 JVM 保證線程安全地初始化;
- 靜態(tài)內(nèi)部類的加載是線程安全的,無需加鎖。
優(yōu)缺點
- ? 優(yōu)點:延遲加載;線程安全;無鎖開銷,性能優(yōu);實現(xiàn)優(yōu)雅,無指令重排風險。
- ? 缺點:無法通過反射完全防止實例被創(chuàng)建(可通過構(gòu)造方法加校驗規(guī)避)。
適用場景
絕大多數(shù)生產(chǎn)環(huán)境場景(平衡性能、優(yōu)雅性、安全性的最優(yōu)解)。
五、枚舉單例模式(最佳實踐)
核心特點:利用枚舉的天然特性實現(xiàn)單例,防反射、防序列化破壞,是《Effective Java》推薦的最優(yōu)方案。
// 枚舉類天然單例,由JVM完全保證唯一性
public enum Singleton {
INSTANCE; // 唯一實例,JVM加載時初始化
// 單例的業(yè)務方法
public void doSomething() {
System.out.println("枚舉單例執(zhí)行業(yè)務邏輯");
}
// 可選:添加自定義屬性/初始化邏輯
private String config;
// 枚舉的構(gòu)造方法默認私有,無需顯式聲明
Singleton() {
// 模擬初始化邏輯(如加載配置)
this.config = "default-config";
}
public String getConfig() {
return config;
}
}
// 使用方式(全局唯一)
public class Client {
public static void main(String[] args) {
Singleton instance1 = Singleton.INSTANCE;
Singleton instance2 = Singleton.INSTANCE;
System.out.println(instance1 == instance2); // true
instance1.doSomething(); // 調(diào)用業(yè)務方法
}
}
關鍵解釋
- 枚舉的實例由 JVM 在類加載時創(chuàng)建,天然線程安全;
- 反射無法創(chuàng)建枚舉實例(Constructor.newInstance() 會拋出 IllegalArgumentException);
- 序列化時,枚舉的 readResolve() 方法被 JVM 重寫,保證反序列化后仍是原實例。
優(yōu)缺點
- ? 優(yōu)點:絕對線程安全;防反射 / 序列化破壞;實現(xiàn)極簡;無需額外處理。
- ? 缺點:非延遲加載(枚舉類加載時即初始化);部分開發(fā)者對枚舉單例的認知不足。
適用場景
對單例唯一性要求極高的場景(如分布式系統(tǒng)、安全框架、核心配置類),是工業(yè)級最佳實踐。
六、擴展:Spring 中的單例實現(xiàn)
Spring 容器中的 @Scope("singleton")(默認作用域)并非直接使用上述代碼,而是通過容器管理實現(xiàn)單例:
1、Spring Bean 默認在容器初始化時創(chuàng)建(類似餓漢式);
2、通過 @Lazy 注解可實現(xiàn)延遲加載(類似懶漢式);
3、Spring 容器保證單例的線程安全,底層結(jié)合了雙重檢查鎖 + 容器緩存機制。
七、總結(jié):選型指南

核心原則:
1、簡單場景用餓漢式,需延遲加載用靜態(tài)內(nèi)部類;
2、對安全性要求極致時,優(yōu)先選擇枚舉單例;
3、避免手動實現(xiàn)復雜的單例邏輯,優(yōu)先利用框架(如 Spring)的單例管理能力。
最終目的:
保證核心對象全局唯一,減少資源消耗;
SpringBoot 核心應用場景:
Spring 容器中默認所有 Bean 為單例(singleton)、ApplicationContext實例全局唯一、核心工具類(如Environment)單例