設計模式(六)創建型:單例模式詳解

設計模式(六)創建型:單例模式詳解

單例模式(Singleton Pattern)是 GoF 23 種設計模式中最簡單卻最常被誤用的創建型模式。其核心價值在于確保一個類在整個應用程序生命周期中僅存在一個實例,并提供一個全局訪問點。它廣泛應用于日志管理器、配置中心、緩存服務、線程池、注冊表、數據庫連接池等需要集中控制資源訪問的場景。雖然實現看似簡單,但其在多線程環境下的安全性、延遲初始化、序列化破壞、反射攻擊等問題使其成為系統架構中一個“看似平凡卻暗藏風險”的關鍵設計。掌握正確的單例實現方式,是構建穩定、高效、可維護系統的基石。

一、單例模式詳細介紹

單例模式解決的是“全局唯一性”與“全局可訪問性”的問題。在許多系統中,某些組件天然具有全局唯一屬性,如系統時鐘、文件系統、打印機后臺服務等。若允許多個實例存在,會導致資源沖突、狀態不一致或性能浪費。單例模式通過控制類的實例化過程,強制保證全局唯一。

該模式包含以下關鍵要素:

  • 私有構造函數(Private Constructor):防止外部通過 new 關鍵字創建實例。
  • 靜態實例字段(Static Instance):保存類的唯一實例,生命周期與類相同。
  • 靜態獲取方法(Static Factory Method):通常命名為 getInstance(),是客戶端獲取單例實例的唯一入口。

根據實例創建時機和線程安全機制的不同,單例模式有多種實現方式:

  1. 餓漢式(Eager Initialization):類加載時即創建實例,線程安全但可能造成資源浪費。
  2. 懶漢式(Lazy Initialization):首次調用 getInstance() 時才創建實例,節省資源但需處理多線程并發問題。
  3. 雙重檢查鎖定(Double-Checked Locking):結合 volatile 關鍵字和同步塊,實現高效且線程安全的延遲初始化。
  4. 靜態內部類(Holder Pattern):利用類加載機制保證線程安全,同時實現延遲加載,是推薦的實現方式。
  5. 枚舉實現(Enum Singleton):由 Java 枚舉機制保證唯一性,防止反射和序列化攻擊,是最安全的實現。

單例模式的核心挑戰在于:

  • 線程安全:在多線程環境下,多個線程同時調用 getInstance() 可能導致創建多個實例。
  • 延遲初始化:是否應在類加載時就創建實例,還是按需創建。
  • 防止反射破壞:通過反射調用私有構造函數可能繞過單例約束。
  • 防止序列化破壞:序列化后反序列化可能生成新實例。
  • 類加載器隔離:在復雜容器(如應用服務器)中,不同類加載器可能導致多個“單例”。

因此,單例模式不僅是設計模式,更是對 JVM 類加載、內存模型、并發控制等底層機制的綜合考驗。

二、單例模式的UML表示

以下是單例模式的標準 UML 類圖:

Singleton
-instance: Singleton
-data: String
-Singleton()
+getInstance()
+doSomething()
+getData()
+setData(data: String)

圖解說明

  • Singleton 類包含一個私有的靜態字段 instance,用于存儲唯一實例。
  • 構造函數 Singleton() 為私有,禁止外部實例化。
  • getInstance() 是靜態方法,返回 instance 的引用,是全局訪問點。
  • doSomething()getData()setData() 是業務方法,所有調用都作用于同一個實例。

三、一個簡單的Java程序實例

以下展示三種典型且安全的單例實現方式:

方式一:靜態內部類(推薦)
/*** 靜態內部類單例(Holder Pattern)* 線程安全,延遲加載,無同步開銷*/
public class SingletonHolder {// 私有構造函數private SingletonHolder() {// 防止反射攻擊if (SingletonInstance.INSTANCE != null) {throw new IllegalStateException("Already initialized.");}}// 靜態內部類,JVM 保證類加載時線程安全且僅加載一次private static class SingletonInstance {private static final SingletonHolder INSTANCE = new SingletonHolder();}public static SingletonHolder getInstance() {return SingletonInstance.INSTANCE;}// 業務方法public void doSomething() {System.out.println("SingletonHolder is doing something...");}
}
方式二:枚舉實現(最安全)
/*** 枚舉單例* 天然防止反射和序列化破壞,代碼最簡潔*/
public enum SingletonEnum {INSTANCE;private String data;public void setData(String data) {this.data = data;}public String getData() {return data;}public void doSomething() {System.out.println("SingletonEnum is doing something with data: " + data);}
}
方式三:雙重檢查鎖定(需謹慎使用)
/*** 雙重檢查鎖定單例* 線程安全,延遲加載,但需 volatile 保證可見性*/
public class SingletonDCL {// volatile 確保多線程下 instance 的可見性和禁止指令重排序private static volatile SingletonDCL instance;private SingletonDCL() {// 防止反射攻擊if (instance != null) {throw new IllegalStateException("Already initialized.");}}public static SingletonDCL getInstance() {if (instance == null) {                    // 第一次檢查synchronized (SingletonDCL.class) {    // 同步塊if (instance == null) {            // 第二次檢查instance = new SingletonDCL(); // JVM 指令重排序可能導致問題,故需 volatile}}}return instance;}public void doSomething() {System.out.println("SingletonDCL is doing something...");}
}
客戶端測試代碼
public class SingletonDemo {public static void main(String[] args) {// 測試靜態內部類單例SingletonHolder s1 = SingletonHolder.getInstance();SingletonHolder s2 = SingletonHolder.getInstance();System.out.println("SingletonHolder: s1 == s2 ? " + (s1 == s2)); // true// 測試枚舉單例SingletonEnum e1 = SingletonEnum.INSTANCE;SingletonEnum e2 = SingletonEnum.INSTANCE;System.out.println("SingletonEnum: e1 == e2 ? " + (e1 == e2)); // true// 測試雙重檢查鎖定單例SingletonDCL d1 = SingletonDCL.getInstance();SingletonDCL d2 = SingletonDCL.getInstance();System.out.println("SingletonDCL: d1 == d2 ? " + (d1 == d2)); // true// 調用業務方法s1.doSomething();e1.setData("Hello Singleton");e1.doSomething();}
}

四、總結

實現方式線程安全延遲加載防反射防序列化推薦度
餓漢式??
懶漢式(同步)??
雙重檢查鎖定需手動需手動????
靜態內部類需手動需手動?????
枚舉實現?????

核心結論

  • 靜態內部類 是 Java 中最優雅、高效的單例實現,推薦在大多數場景使用。
  • 枚舉實現 是最安全的,尤其適用于需要防止反射和序列化破壞的場景(如權限管理、許可證控制)。
  • 雙重檢查鎖定 雖高效,但實現復雜,易出錯,除非有特殊性能要求,否則不推薦手動實現。
  • 單例模式應謹慎使用,避免濫用導致全局狀態污染、測試困難、模塊耦合。

架構師洞見:
單例模式是“雙刃劍”——它提供便利,也埋下隱患。架構師必須清醒認識到:單例本質上是一種全局狀態(Global State),會破壞封裝性、增加模塊耦合、阻礙單元測試(難以 Mock)、影響可擴展性。在現代依賴注入(DI)框架(如 Spring)中,“容器管理的單例”已取代“手動編碼的單例”。Spring 中的 @Component + @Scope("singleton") 由容器統一管理生命周期,解耦了業務邏輯與實例化邏輯,是更優的實踐。

未來趨勢是:避免手寫單例,優先使用框架容器管理對象生命周期。對于確實需要全局唯一組件的場景,應優先考慮使用枚舉或靜態內部類,并加入防御性代碼防止反射攻擊。在微服務架構中,單例的“應用級唯一”可能演變為“服務實例級唯一”,甚至通過分布式協調服務(如 ZooKeeper、etcd)實現“集群級唯一”。

掌握單例模式,不僅是學會幾種寫法,更是理解資源管理、并發控制、系統可測試性與可維護性之間的權衡。作為架構師,應引導團隊合理使用單例,避免其成為技術債務的源頭。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/91351.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/91351.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/91351.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

PostgreSQL AND OR 操作符詳解

PostgreSQL AND & OR 操作符詳解 在數據庫查詢中,AND 和 OR 是兩種常見的邏輯操作符,用于組合多個查詢條件。PostgreSQL 作為一款功能強大的開源關系型數據庫管理系統,同樣支持這些操作符。本文將詳細介紹 PostgreSQL 中的 AND 和 OR 操作符,并探討它們在查詢中的應用…

RabbiteMQ安裝-ubuntu

Ubuntu 1.安裝Erlang RabbitMQ需要Erlang語言的支持,在安裝RabbitMQ之前需要安裝Erlang #更新軟件包 sudo apt-get update#安裝erlang sudo apt-get install erlang查看erlang版本 roothcss-ecs-027f:/# erl Erlang/OTP 24 [erts-12.2.1] [source] [64-bit] [sm…

Linux驅動20 --- FFMPEG視頻API

目錄 一、FFMPEG 視頻 API 的使用 1.1 介紹 1.2 整體編程過程 獲取核心上下文指針 打開輸入流文件 獲取輸入流 獲取編碼器 初始化解碼器 申請輸出流指針 獲取顯示數據空間大小 申請輸出顯示空間 綁定輸出流和輸出顯示空間 申請格式轉換上下文 申請輸入流指針 讀取一幀數據 發…

OpenBayes 一周速覽丨Self Forcing 實現亞秒級延遲實時流視頻生成;邊緣AI新秀,LFM2-1.2B采用創新性架構超越傳統模型

公共資源速遞 This Weekly Snapshots ! 5 個公共數據集: * AF-Chat 音頻對話文本數據集 * ArtVIP 機器交互式圖像數據集 * Updesh 印度語合成文本數據集 * Medical Information 藥品信息數據集 * Nemotron-Math-HumanReasoning 數學推理數據集…

[NOIP2002 提高組] 均分紙牌

題目描述有N堆紙牌,編號分別為 1,2,…,N。每堆上有若干張,但紙牌總數必為N的倍數。可以在任一堆上取若干張紙牌,然后移動。移牌規則為:在編號為1堆上取的紙牌,只能移到編號為2的堆上;在編號為N的堆上取的紙…

【音視頻】WebRTC-Web 音視頻采集與播放

一、打開攝像頭 打開攝像頭首先需要有一個html的video標簽&#xff1a; id "local-video"&#xff0c;是為了后續的js腳本調用這個對象autoplay是設置打開后自動播放&#xff0c;playsinline則是為了兼容移動端 <video id "local-video" autoplay p…

數據治理平臺如何選?深度解析國產化全棧方案與行業落地實踐

“數據治理平臺廠商有哪些&#xff1f;”國內主流廠商包括阿里云、華為、百分點科技等&#xff0c;各有所長。其中&#xff0c;百分點科技憑借在應急管理、智慧公安及央國企數字化領域的深度實踐&#xff0c;打造了行業特色鮮明的數據治理解決方案。百分點科技的數據治理解決方…

限流算法詳解:固定窗口、滑動窗口、令牌桶與漏桶算法全面對比

限流&#xff08;Rate Limiting&#xff09;是保障系統穩定性和服務質量的關鍵機制&#xff0c;尤其在高并發、突發流量、攻擊防護等場景中至關重要。本文將詳細介紹四種主流限流算法&#xff1a;固定窗口&#xff08;Fixed Window&#xff09;滑動窗口&#xff08;Sliding Win…

Sentinel 搭建應用層面與網關層面的流控保護

源碼&#xff1a;妖精的尾巴/spring-cloud-alibaba Nacos 和 Sentinel Dashboard 我這里全是使用window 本地運行的&#xff0c;需要自行下載運行 服務層面&#xff1a; 當你在某個具體的服務上使用Sentinel時&#xff0c;更多的是關注該服務內部資源的保護。例如&#xff0c…

純血鴻蒙 AudioRenderer+AudioCapturer+RingBuffer 實現麥克風采集+發聲

總共兩個類&#xff0c;放到代碼里&#xff0c;就可以快速完成K歌的效果&#xff0c;但應用層這么做延遲是比較高的&#xff0c;只是做一個分享。 類代碼 import { audio } from kit.AudioKit; import { BusinessError } from kit.BasicServicesKit; import { AudioBufferFlow,…

洛谷 P1601 A+B Problem(高精)普及-

題目描述 高精度加法&#xff0c;相當于 ab problem&#xff0c;不用考慮負數。 輸入格式 分兩行輸入。a,b≤10500a,b \leq 10^{500}a,b≤10500。 輸出格式 輸出只有一行&#xff0c;代表 ababab 的值。 輸入輸出樣例 #1 輸入 #1 1 1輸出 #1 2輸入輸出樣例 #2 輸入 #2 1001 909…

Matrix Theory study notes[6]

文章目錄linear spacereferenceslinear space a basis of linear space VkV^kVk,which is x1,x2,...xkx_1,x_2,...x_kx1?,x2?,...xk?,can be called as a coordinate system.let vector v∈Vkv \in V^kv∈Vk and it can be linear expressed on this basis as va1x1a2x2...…

專線與專線之間的區別

下面我們從定義、技術特點、適用場景、優缺點等多個維度來詳細對比&#xff1a;? 一、四種方案簡要定義技術方案定義MPLS 專線運營商基于 MPLS 技術提供的私有虛擬網絡&#xff0c;邏輯隔離、安全可靠VPN over Internet利用公網加密通道&#xff08;如IPSec&#xff09;構建虛…

Git工作流:團隊協作的最佳實踐

目錄 一、什么是 Git 工作流&#xff1f;為什么需要它&#xff1f; 二、基礎&#xff1a;Git 分支核心概念 三、主流 Git 工作流實戰指南 1. 集中式工作流&#xff08;Centralized Workflow&#xff09;&#xff1a;適合小團隊 / 新手 操作步驟&#xff1a; 優缺點&#…

算法競賽階段二-數據結構(35)數據結構單鏈表模擬實現

//鏈表--鏈式存儲的線性表 //存信息和下一個節點位置&#xff0c;數據域和指針域合起來叫節點 //帶頭&#xff08;哨兵位&#xff09;下標為0 //單向&#xff0c;雙向&#xff0c;循環鏈表 //實現 單 //倆足夠大數組 // elem&#xff0c;數據域 // next &#xff0c;指針域…

《Computational principles and challenges in single-cell data integration》

1. 引言&#xff1a;單細胞數據整合的背景與重要性單細胞基因組學技術&#xff08;如scRNA-seq、scATAC-seq等&#xff09;近年來快速發展&#xff0c;能夠以單細胞分辨率揭示細胞異質性和分子機制。然而&#xff0c;不同實驗、樣本和數據模態&#xff08;如RNA表達、DNA甲基化…

蔚來汽車攜手通義靈碼入選 2025 世界人工智能大會標桿案例

7月28日&#xff0c;在2025年世界人工智能大會上&#xff0c;通義靈碼助力蔚來汽車研發效能升級成功入選2025年“人工智能”行業標桿案例薈萃。蔚來汽車已有近 1000 名工程師常態化使用通義靈碼&#xff0c;AI 生成代碼占比超 30%&#xff0c;尤其在蔚來“天探”AI自檢系統的建…

Spring Boot中的this::語法糖詳解

文章目錄前言什么是方法引用&#xff08;Method Reference&#xff09;基本語法方法引用的四種類型1. 靜態方法引用2. 實例方法引用&#xff08;特定對象&#xff09;3. 實例方法引用&#xff08;任意對象&#xff09;4. 構造器引用this::在Spring Boot中的應用場景1. Service層…

VitePress學習筆記

VitePress學習筆記VitePress學習搭建和運行編寫內容mdvue配置站點配置配置searchsearch 提示詞替換使用第三方主題自定義主題設置文檔根目錄國際化文檔navsidebarsearch其他插件vitepress插件markdown-it插件項目開發原始需求和方案自動化流程權限限制VitePress學習 搭建和運行…

C#_創建自己的MyList列表

定義一個數據自己的列表MyList 使用上述描述列表的方式(數組) 列表內也要定義屬于自己的方法 例如 Sort排序 Add添加 等等....思路┌─────────────────────────────────────────────────────────────────…