当前位置: 首页 > news >正文

设计模式实战篇(一):彻底搞懂 Singleton 单例模式

💬 关键词:创建型设计模式、线程安全、JVM 类加载机制、反射防护、Spring 单例


一、什么是单例模式?

单例模式(Singleton Pattern) 是一种最经典的“创建型”设计模式,确保在整个系统生命周期中,某个类只有一个实例,并为全局提供访问点。

💡 举例:

  • 一个系统只有一个配置管理器。
  • 日志模块全局共享一个 Logger。
  • 线程池、数据库连接池等都是单例。

二、核心设计原则

原则含义
构造器私有化防止外部随意实例化
静态变量持有实例确保全局唯一
静态方法提供访问点控制访问
线程安全控制多线程下仍然唯一

三、UML 类图

┌────────────────────────┐
│       Singleton        │
├────────────────────────┤
│ - instance : Singleton │
├────────────────────────┤
│ + getInstance()        │
└────────────────────────┘

四、单例的五种实现方式对比

实现方式是否懒加载是否线程安全性能是否推荐备注
饿汉式👍⚠️ 否占用资源
懒汉式👎❌ 否非线程安全
双重检查锁(DCL)👍👍推荐使用
静态内部类👍👍👍🌟 推荐简洁优雅
枚举单例👍👍👍🌟🌟 强烈推荐最安全方案

五、代码示例

1️⃣ 饿汉式

public class SingletonEager {private static final SingletonEager INSTANCE = new SingletonEager();private SingletonEager() {}public static SingletonEager getInstance() {return INSTANCE;}
}

特点:

  • 类加载时就创建实例。

  • 天然线程安全,但浪费内存。


2️⃣ 懒汉式(Lazy Initialization)- 非线程安全

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

问题:
多个线程同时进入 if (instance == null),可能创建多个实例。


3️⃣ 双重检查锁(DCL)

public class SingletonDCL {private static volatile SingletonDCL instance;private SingletonDCL() {}public static SingletonDCL getInstance() {if (instance == null) {synchronized (SingletonDCL.class) {if (instance == null) {instance = new SingletonDCL();}}}return instance;}
}

为什么需要 volatile

对象实例化可能被编译器重排序:

1️⃣ 分配内存
2️⃣ 调用构造函数
3️⃣ 引用指向内存地址

若 2️⃣ 与 3️⃣ 调换顺序 → 其他线程可能拿到一个“未完全初始化”的对象。
volatile 关键字禁止这种指令重排。

原理解析:

  • volatile 保证禁止指令重排。

  • 双层检查保证性能与安全。

ThreadAThreadBJVM检查 instance 是否为 null为 null获取锁 & 创建实例再次检查 instance已创建,直接返回ThreadAThreadBJVM

4️⃣ 静态内部类(推荐实现)

public class SingletonInner {private SingletonInner() {}private static class Holder {private static final SingletonInner INSTANCE = new SingletonInner();}public static SingletonInner getInstance() {return Holder.INSTANCE;}
}

JVM 原理:

  • Holder 类不会在外部类加载时立即加载。
  • 当调用 getInstance() 时才加载 Holder
  • JVM 保证类加载过程的线程安全性。

5️⃣ 枚举单例(最优雅实现)

public enum SingletonEnum {INSTANCE;public void doSomething() {System.out.println("Enum Singleton working!");}
}

🧠 原理:

  • 枚举类由 JVM 保证只加载一次。

  • 天然防止反射与反序列化攻击。


六、线程安全分析

实现是否线程安全说明
饿汉式类加载时完成实例化
懒汉式多线程会创建多个实例
DCL结合 volatile 可安全高效
静态内部类类加载机制保证安全
枚举JVM 保证枚举类实例唯一性

七、反射与序列化

1️⃣ 反射破坏单例

即使构造函数私有,也可通过反射调用创建多个实例。
解决办法:在构造函数中检测是否已有实例。

if (instance != null) {throw new RuntimeException("禁止通过反射创建对象!");
}

2️⃣ 序列化破坏单例

反序列化会生成新对象。
解决办法:添加 readResolve() 方法。

protected Object readResolve() {return getInstance();
}

八、应用场景

场景示例
系统配置类ConfigManager
日志管理Logger
线程池ThreadPoolExecutor
缓存RedisManager
数据库连接池DataSource

日志管理器(Logger)

public class LoggerManager {private LoggerManager() {}private static class Holder {private static final LoggerManager INSTANCE = new LoggerManager();}public static LoggerManager getInstance() {return Holder.INSTANCE;}public void log(String msg) {System.out.println("[LOG] " + msg);}
}

使用示例:

public class App {public static void main(String[] args) {LoggerManager logger = LoggerManager.getInstance();logger.log("Application started.");}
}

控制台输出:

[LOG] Application started.

九、优缺点

优点缺点
全局唯一对象,节省资源难以扩展,测试困难
统一访问点,便于管理并发复杂度高
支持延迟加载(部分实现)潜在隐藏依赖

十、单例在 Spring 框架中的体现

场景描述
IOC 容器默认作用域Spring Bean 默认是单例(singleton)
Bean 生命周期管理容器初始化时加载实例
线程安全性Spring 通过容器同步机制控制实例唯一性

📘 源码示例(AbstractBeanFactory.java)

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

十一、总结

实现方式是否推荐备注
饿汉式占用资源
懒汉式非线程安全
双检锁高性能、安全
静态内部类推荐方式
枚举单例最优雅实现

最佳实践:推荐使用“静态内部类”或“枚举单例”。


十一、单例模式的演进对比图(示意)

创建时机 ───────────────────────────►
饿汉式 ──┬─────────────► 立即创建
懒汉式 ──┬─────► 延迟创建(不安全)
DCL ─────┬─────► 延迟创建 + 锁优化
内部类 ──┬─────► JVM 加载机制保证线程安全
枚举 ────┬─────► JVM 保证唯一性

🧠 结语:单例模式虽然简单,但它是理解 Java 内存模型、线程安全、类加载机制的绝佳入门实践。

http://www.dtcms.com/a/600737.html

相关文章:

  • 什么是电子商务网站建设网站建设的一些背景图片
  • 一个有 IP 的服务端监听了某个端口,那么他的 TCP 最大链接数是多少
  • K8s常用排障调试工具 入侵排查 kubectl debug 命令详解
  • yield(放弃优先权)
  • 基于MATLAB的噪声图像处理方案
  • 做动态logo网站做网站有底薪吗
  • C语言编译器最新版 | 全面提升性能与兼容性
  • 厦门网站建设建设公司免费动漫软件app下载大全
  • 开源模型应用落地-FastAPI-助力模型交互-进阶篇-中间件(四)
  • springBoot (springCloud2025)集成redisCluster 集群
  • Redis在Windows上测试运行Memurai
  • windows ubuntu双系统下卸载ubuntu
  • 零基础入门C语言之C语言实现数据结构之双向链表
  • 初次接触 LoRA 技术
  • 西安哪家网站公司做的比较好做网页制作的价格
  • 【OpenCV + VS 】图像通道分离与合并
  • 【超分辨率专题】HYPIR:扩散模型先验与 GAN 对抗训练相结合的新型图像复原框架
  • 【ZeroRange WebRTC】kvsWebrtcClientMaster 获取 ICE 服务器配置解析
  • 手机网站建设liednswordpress改模板教程视频
  • Chrome V3 插件开发:监听并转发 API 请求
  • OpenCV 图像处理与键盘交互
  • 长沙理工《人工智能基础A》实验(上机)报告实验三 电商数据可视化/图像处理
  • Elasticsearch 的结构化文档配置 - 递归分块实践
  • 如何在IIS中配置HTTP重定向
  • elasticsearch 安装 repository-oss 插件
  • 宝安做网站哪家好德阳网站建设熊掌号
  • 输入10个整数存放于数组中,并将最小的数与数组的第一个元素交换,最大的数与数组的最后一个元素交换
  • 从 WAL 到 Fluss->Flink CDC Postgres Connector 端到端同步实战
  • 数据结构 图 的邻接表建立
  • C++CUDA实战:通过两个图像算法,搞懂了GPU编程