设计模式(六)创建型:单例模式详解

设计模式(六)创建型:单例模式详解

单例模式(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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈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添加 等等....思路┌─────────────────────────────────────────────────────────────────…