单例模式:全局唯一实例的设计艺术

引言:为什么需要单例模式

在软件开发中,某些对象只需要一个全局实例

  • 数据库连接池
  • 配置管理器
  • 日志记录器
  • 线程池
  • 缓存系统

使用new关键字多次创建这些对象会导致:

多次创建
资源浪费
状态不一致
并发冲突

单例模式正是为解决这类问题而生的设计模式。它确保一个类只有一个实例,并提供全局访问点。本文将深入剖析单例模式的原理、实现及高级应用场景。


一、模式定义与核心思想

1.1 官方定义

单例模式 (Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。

1.2 设计哲学

客户端
单例实例
客户端
客户端

核心原则

  1. 私有构造器:防止外部实例化
  2. 静态实例:全局唯一实例
  3. 全局访问点:提供获取实例的方法

二、单例模式实现方式大全

2.1 实现方案对比

实现方式线程安全延迟加载序列化安全反射安全复杂度
饿汉式★☆☆
懒汉式(非线程安全)★☆☆
同步方法★★☆
双重检查锁★★★
静态内部类★★☆
枚举★☆☆

2.2 饿汉式(Eager Initialization)

public class EagerSingleton {// 类加载时即初始化private static final EagerSingleton INSTANCE = new EagerSingleton();// 私有构造器private EagerSingleton() {}// 全局访问点public static EagerSingleton getInstance() {return INSTANCE;}
}

特点

  • 线程安全(JVM保证类加载的线程安全)
  • 不支持延迟加载
  • 简单直接

2.3 懒汉式(Lazy Initialization - 非线程安全)

public class UnsafeLazySingleton {private static UnsafeLazySingleton instance;private UnsafeLazySingleton() {}public static UnsafeLazySingleton getInstance() {if (instance == null) {instance = new UnsafeLazySingleton();}return instance;}
}

风险:多线程环境下可能创建多个实例

2.4 同步方法(Thread-Safe Lazy)

public class SynchronizedSingleton {private static SynchronizedSingleton instance;private SynchronizedSingleton() {}// 同步方法保证线程安全public static synchronized SynchronizedSingleton getInstance() {if (instance == null) {instance = new SynchronizedSingleton();}return instance;}
}

缺点:每次获取实例都需要同步,性能差

2.5 双重检查锁(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;}
}

关键点

  1. volatile防止指令重排序
  2. 两次判空减少同步开销

2.6 静态内部类(Initialization-on-demand Holder)

public class HolderSingleton {private HolderSingleton() {}// 静态内部类持有实例private static class SingletonHolder {static final HolderSingleton INSTANCE = new HolderSingleton();}public static HolderSingleton getInstance() {return SingletonHolder.INSTANCE;}
}

原理:利用类加载机制保证线程安全,实现延迟加载

2.7 枚举(Enum Singleton - 最佳实践)

public enum EnumSingleton {INSTANCE;// 业务方法public void businessMethod() {System.out.println("Singleton business logic");}
}

优势

  • 线程安全
  • 序列化安全
  • 反射安全
  • 简洁明了

三、单例模式进阶挑战

3.1 序列化与反序列化安全

public class SerializableSingleton implements Serializable {private static final long serialVersionUID = 1L;private static SerializableSingleton instance = new SerializableSingleton();private SerializableSingleton() {}public static SerializableSingleton getInstance() {return instance;}// 防止反序列化创建新实例protected Object readResolve() {return instance;}
}

3.2 反射攻击防护

public class ReflectionProofSingleton {private static ReflectionProofSingleton instance;private ReflectionProofSingleton() {// 防止反射创建实例if (instance != null) {throw new IllegalStateException("Singleton already initialized");}}public static synchronized ReflectionProofSingleton getInstance() {if (instance == null) {instance = new ReflectionProofSingleton();}return instance;}
}

3.3 克隆安全

public class CloneSafeSingleton implements Cloneable {private static CloneSafeSingleton instance = new CloneSafeSingleton();private CloneSafeSingleton() {}public static CloneSafeSingleton getInstance() {return instance;}@Overrideprotected Object clone() throws CloneNotSupportedException {throw new CloneNotSupportedException("Singleton cannot be cloned");}
}

四、多线程环境下的单例

4.1 性能对比测试

barCharttitle 单例模式性能对比(1000万次获取)x-axis 实现方式y-axis 时间(ms)series 耗时EagerSingleton: 32EnumSingleton: 35HolderSingleton: 38DCLSingleton: 45SynchronizedSingleton: 1200

4.2 单例与线程池

public class ThreadPoolSingleton {private static final int CORE_POOL_SIZE = 5;private static volatile ExecutorService instance;private ThreadPoolSingleton() {}public static ExecutorService getInstance() {if (instance == null) {synchronized (ThreadPoolSingleton.class) {if (instance == null) {instance = new ThreadPoolExecutor(CORE_POOL_SIZE,CORE_POOL_SIZE,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());}}}return instance;}
}

4.3 分布式环境挑战

服务A
Redis/ZooKeeper
服务B

解决方案

  1. 使用分布式锁实现全局单例
  2. 依赖外部存储维护状态

五、单例模式应用场景

5.1 典型应用场景

场景单例应用优势
配置管理全局配置读取统一配置源
日志系统日志记录器避免重复创建
数据库连接连接池管理资源复用
缓存系统全局缓存数据一致性
硬件接口设备控制避免冲突

5.2 使用时机判断

当满足以下条件时考虑单例模式:

  1. 类需要全局唯一实例
  2. 需要严格控制资源访问
  3. 需要共享状态或数据
  4. 需要频繁访问的对象

5.3 不适用场景

  1. 需要多实例的类
  2. 需要扩展的子类
  3. 测试驱动开发(难以模拟)
  4. 分布式系统(需特殊处理)

六、模式优劣辩证

6.1 优势 ✅

35% 25% 20% 15% 5% 单例模式优势 资源优化 状态一致性 全局访问 避免重复创建 减少内存占用

6.2 劣势 ❌

  1. 违反单一职责:兼具创建和管理功能
  2. 测试困难:难以模拟和替换
  3. 隐藏依赖:增加耦合度
  4. 并发挑战:需要额外处理线程安全
  5. 生命周期管理:何时销毁实例

七、单例模式与依赖注入

7.1 单例 vs 依赖注入容器

直接调用
注入
使用
传统单例
单例类
DI容器
客户端
单例Bean

7.2 Spring中的单例

@Service // Spring默认单例作用域
public class OrderService {// 业务逻辑
}@RestController
public class OrderController {private final OrderService orderService;// 通过构造器注入单例public OrderController(OrderService orderService) {this.orderService = orderService;}
}

7.3 最佳整合实践

public class DatabaseConnection {private static DatabaseConnection instance;private DatabaseConnection() {}public static DatabaseConnection getInstance() {if (instance == null) {synchronized (DatabaseConnection.class) {if (instance == null) {instance = new DatabaseConnection();}}}return instance;}// 注册到Spring容器@Beanpublic DatabaseConnection databaseConnection() {return DatabaseConnection.getInstance();}
}

八、在开源框架中的应用

8.1 Java Runtime类

public class Runtime {private static Runtime currentRuntime = new Runtime();public static Runtime getRuntime() {return currentRuntime;}private Runtime() {}
}

8.2 Log4j2 LoggerContext

public class LoggerContext {private static LoggerContext context;public static LoggerContext getContext() {if (context == null) {synchronized (LoggerContext.class) {if (context == null) {context = new LoggerContext();}}}return context;}
}

8.3 Spring ApplicationContext

public class AnnotationConfigApplicationContext {// 虽然不是严格单例,但通常作为单例使用
}// 典型使用方式
@SpringBootApplication
public class MyApp {public static void main(String[] args) {// 创建单例ApplicationContextApplicationContext context = SpringApplication.run(MyApp.class, args);}
}

九、单例模式反模式与陷阱

9.1 常见错误实现

// 反例1:public字段暴露
public class PublicFieldSingleton {public static final PublicFieldSingleton INSTANCE = new PublicFieldSingleton();private PublicFieldSingleton() {}
}// 反例2:静态块未处理异常
public class StaticBlockSingleton {private static StaticBlockSingleton instance;static {try {instance = new StaticBlockSingleton();} catch (Exception e) {// 未处理异常}}private StaticBlockSingleton() {}public static StaticBlockSingleton getInstance() {return instance;}
}

9.2 单例滥用案例

// 反例:将业务服务设计为单例
public class UserService {private static UserService instance;private UserService() {}// 单例访问点public static synchronized UserService getInstance() {if (instance == null) {instance = new UserService();}return instance;}// 业务方法public void registerUser(User user) {// ...}
}

问题

  1. 难以扩展
  2. 无法模拟测试
  3. 状态污染风险

十、最佳实践指南

10.1 实现选择建议

需要单例
需要延迟加载
需要序列化/反射安全
枚举实现
性能敏感
双重检查锁
静态内部类
需要序列化/反射安全
饿汉式

10.2 线程安全实践

  1. 优先使用不可变状态

    public class ImmutableSingleton {private final Map<String, String> config;private ImmutableSingleton() {// 初始化后不再修改config = loadConfigFromFile();}public String getConfig(String key) {return config.get(key);}
    }
    
  2. 使用ThreadLocal实现线程单例

    public class ThreadLocalSingleton {private static final ThreadLocal<ThreadLocalSingleton> instance = ThreadLocal.withInitial(ThreadLocalSingleton::new);private ThreadLocalSingleton() {}public static ThreadLocalSingleton getInstance() {return instance.get();}
    }
    

10.3 测试策略

public class DatabaseConnection {private static DatabaseConnection instance;// 测试钩子static void setTestInstance(DatabaseConnection testInstance) {instance = testInstance;}// 重置为生产实例static void reset() {instance = null;}
}// 测试类
class DatabaseConnectionTest {@AfterEachvoid tearDown() {DatabaseConnection.reset();}@Testvoid testSingleton() {DatabaseConnection.setTestInstance(mock(DatabaseConnection.class));// 执行测试}
}

十一、总结:单例模式的核心价值

单例模式通过全局唯一实例实现了:

资源优化
提高性能
状态一致性
保证数据准确
全局访问
简化调用

设计启示
单例不是银弹,而是特定场景下的精密工具 - 用对场景比实现更重要

正如《设计模式》作者GoF所强调:

“单例模式确保一个类仅有一个实例,并提供一个访问它的全局访问点。这个模式在需要控制资源或者协调操作时特别有用”


扩展思考

  1. 如何在微服务架构中实现全局单例?
  2. 单例模式如何与反应式编程结合?
  3. 单例对象的内存泄漏如何预防?

附录:单例模式快速参考卡

场景推荐实现注意事项
简单应用枚举单例最佳实践首选
延迟加载静态内部类简洁安全
高性能要求双重检查锁需加volatile
线程隔离ThreadLocal非全局单例
Spring环境@Bean单例利用容器管理

单例模式是构建高效、一致系统的关键工具,在资源管理、配置控制等场景中具有不可替代的价值

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/85219.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/85219.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

性能优化 - 案例篇:11种优化接口性能的通用方案

文章目录 Pre1. 加索引&#xff1a;最低成本&#xff0c;最大收益常见问题&#xff1a;工具命令&#xff1a;建议&#xff1a; 2. SQL 优化&#xff1a;比加索引再进阶一步常见 5 类问题&#xff1a;实用建议&#xff1a; 3. 远程调用&#xff1a;从串行改并行&#xff0c;性能…

Kafka - 并发消费拉取数据过少故障分析

文章目录 背景与问题描述原理与原因分析参数优化思路示例配置验证与监控实践注意事项与风险总结 背景与问题描述 场景描述 使用 Spring Boot Spring Kafka&#xff0c;注解 KafkaListener(topics..., id..., ...)&#xff0c;批量监听&#xff08;方法签名为 public void doHa…

开源 Arkts 鸿蒙应用 开发(二)封装库.har制作和应用

文章的目的为了记录使用Arkts 进行Harmony app 开发学习的经历。本职为嵌入式软件开发&#xff0c;公司安排开发app&#xff0c;临时学习&#xff0c;完成app的开发。开发流程和要点有些记忆模糊&#xff0c;赶紧记录&#xff0c;防止忘记。 相关链接&#xff1a; 开源 Arkts …

Qt基础相关

模态对话框和非模态对话框 在一个页面进行交互时弹出的一个新页面&#xff0c;新页面不堵塞旧页面的交互&#xff0c;这就是非模态对话框。 模态对话框 模态对话框就是当该对话框弹出后会阻塞其他窗口的响应事件&#xff0c;必须先关闭该对话框&#xff0c;其他窗口才会继续…

《汇编语言:基于X86处理器》第2章 x86处理器架构

本章重点是与 x86 汇编语言相关的底层硬件。有说法认为&#xff0c;汇编语言是直接与机器交流的理想软件工具。如果是真的&#xff0c;那么汇编程序员就必须非常熟悉处理器的内部结构与功能。本章将讨论指令执行时处理器内部发生的一些基本操作&#xff0c;以及操作系统如何加载…

最小生成树算法的解题思路与 C++ 算法应用

一、最小生成树算法针对问题类型及概述 先来简要陈述一下树的概念&#xff1a;一个由 N N N 个点和 N − 1 N-1 N−1 条边组成的无向连通图。由此&#xff0c;我们可以得知生成树算法的概念&#xff1a;在一个 N N N 个点的图中找出一个由 N − 1 N-1 N−1 条边组成的树。…

feign.FeignException$NotFound: [404 ] during [POST] to [http://ti/ti/v1/i/se

feign.FeignException$NotFound: [404 ] during [POST] to [http://ti/ti/v1/i/send 原因&#xff1a;多个地方注册 FeignClient(name “ti”, path “/ti/v1/i/send/repeat”) 解决&#xff1a;删除一个即可

Mac m1 通过docker镜像安装kafka

kafka依赖zookeeper&#xff0c;因此需要使用docker同时安装zookeeper和kafka。 macOS的docker在容器和宿主之间无法通过ip直接通信&#xff0c;因此在安装的时候需要特殊注意与ip相关的设置。当容器需要访问宿主ip时&#xff0c;需要使用docker.for.mac.host.internal或者host…

01初始uni-app+tabBar+首页

初始uni-apptabBar首页 1. uni-app1.1 新建uni-app项目1.2 目录结构1.3 把项目配置运行到微信开发者工具 2. tabBar3.1 首页3.1 配置网络请求3.2 轮播图区域3.3 分类导航区域3.4 楼层区域 1. uni-app ​ uni-app 是使用 Vue.js 开发前端应用的框架。开发者编写一套代码&#x…

微信小程序,微信授权手机号码

uniapp中index.vue: <template><view class"content"><button open-type"getPhoneNumber" getphonenumber"getPhoneNumber"type"primary">授权手机号登录 </button></view></template><scrip…

数据结构 学习 图 2025年6月14日 12点57分

搜索算法 深度优先搜索 一种用于遍历或搜索树或图的算法。它沿着树的深度遍历树的节点&#xff0c;尽可能深的搜索树的分支。 DFS核心思想 深度优先&#xff1a;尽可能深地搜索树的分支 回溯思想&#xff1a;当节点v的所在边都已被探寻过&#xff0c;搜索将回溯到发现节点v的…

H3C路由器使用PBR 实现两条互联网专线互为备份

实验拓扑 图 1-1 注&#xff1a;如无特别说明&#xff0c;描述中的 R1 或 SW1 对应拓扑中设备名称末尾数字为 1 的设备&#xff0c;R2 或 SW2 对应拓扑中设备名称末尾数字为 2 的设备&#xff0c;以此类推&#xff1b;另外&#xff0c;同一网段中&#xff0c;IP 地址的主机位为…

深化信创生态布局!聚铭网络与海量数据完成产品兼容性互认证

近日&#xff0c;聚铭网络成功与海量数据完成了一系列产品的兼容性互认证&#xff0c;并获得了由海量数据颁发的产品兼容互认证书。这一成就标志着双方在技术整合方面迈出了重要一步。 经过全面严格的测试&#xff0c;聚铭网络自主研发的安全系列产品&#xff0c;包括聚铭下一…

Unity AR+ 百度AI 实现 物体识别与对应英文翻译

一、前言 我目前实现了拍照保存到手机的功能 我想进一步优化&#xff0c;实现通过手机摄像头实时识别眼前的物体&#xff0c;显示对应的英文的功能。 1.项目技术栈&#xff1a;Unity 2022.3.53 Vuforia 11 百度物体识别API 百度翻译API 2.功能目标&#xff1a;使用手机摄像…

Vue.js第二节

计算属性、事件绑定、条件判断、遍历循环 计算属性&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0">…

从开源代码入场无人机学术研究到商业化市场的全路径指南-优雅草卓伊凡

从开源代码入场无人机学术研究到商业化市场的全路径指南-优雅草卓伊凡 引言&#xff1a;开源代码在无人机研究中的重要性 优雅草卓伊凡在这里告诉大家&#xff0c;如果真的要开始进入无人机领域&#xff0c;我们需要一步步开始研究。目前先去看看开源无人机代码是尤为重要的&…

window11中开启ubuntu22.04子系统

一、启用Windows子系统 打开控制面板 选择程序然后点击“启用或关闭Windows功能” 勾选如下2项&#xff0c;点击确定 二、安装内核升级包 打开链接https://wslstorestorage.blob.core.windows.net/wslblob/wsl_update_x64.msi下载内核升级包&#xff0c;打开后安装、重启电脑…

80Qt窗口_对话框

目录 5. 对话框 5.1 对话框介绍 用例1&#xff1a; 用例2&#xff1a; 用例3&#xff1a; 用例4&#xff1a; 5.2 对话框的分类 5.2.1 模态对话框 5.2.2 ⾮模态对话框 5. 对话框 5.1 对话框介绍 对话框是 GUI 程序中不可或缺的组成部分。⼀些不适合在主窗⼝实现的功…

Pyenv 跟 Conda 还有 Poetry 有什么区别?各有什么不同?

pyenv、Conda 和 Poetry 是 Python 生态中常用的工具&#xff0c;但它们的核心功能和用途不同&#xff0c;通常可以结合使用。以下是它们的区别和特点&#xff1a; 1. pyenv 用途&#xff1a;管理多个 Python 解释器版本。 核心功能&#xff1a; 安装不同版本的 Python&#x…

数学符号和标识中英文列表(含义与示例)

数学符号和标识的参考&#xff0c;涵盖了数学的各个主要分支&#xff0c;并提供清晰的定义和示例&#xff0c;方便快速查找和学习收藏。 目录 基础数学符号几何符号代数符号线性代数符号概率与统计符号集合论符号逻辑符号微积分与分析符号数字与字母符号 特点 中英对照&…