【设计模式】【创建型模式】单例模式(Singleton)

news/2025/2/23 3:45:12/

👋hi,我不是一名外包公司的员工,也不会偷吃茶水间的零食,我的梦想是能写高端CRUD
🔥 2025本人正在沉淀中… 博客更新速度++
👍 欢迎点赞、收藏、关注,跟上我的更新节奏
🎵 当你的天空突然下了大雨,那是我在为你炸乌云

文章目录

一、入门

什么是单例模式

单例模式是一种设计模式,确保一个类只有一个实例,并提供一个全局访问点。它常用于需要全局唯一对象的场景,如配置管理、连接池等。

为什么要单例模式

  1. 节省资源
    • 场景:某些对象创建和销毁成本高,如数据库连接池、线程池等。
    • 原因:单例模式确保这些资源只创建一次,避免重复创建和销毁,节省系统资源。
  2. 全局访问点
    • 场景:需要在多个模块或组件中共享同一个对象,如配置管理器、日志记录器等。
    • 原因:单例模式提供一个全局访问点,方便不同模块共享同一实例,避免频繁传递对象。
  3. 保持一致性
    • 场景:需要确保某些操作或状态在整个应用中保持一致,如缓存管理、计数器等。
    • 原因:单例模式确保只有一个实例,避免多个实例导致状态不一致。
  4. 控制实例数量
    • 场景:某些情况下,限制实例数量是必要的,如打印机管理、文件系统等。
    • 原因:单例模式确保只有一个实例存在,避免资源冲突或竞争条件。
  5. 简化设计
    • 场景:某些设计模式(如工厂模式、抽象工厂模式)中,单例模式可以简化对象创建和管理。
    • 原因:单例模式减少对象创建的复杂性,使设计更简洁。

如何实现单例模式

  1. 私有构造函数:防止外部通过 new 创建实例。
  2. 静态私有实例:类内部持有唯一的实例。
  3. 静态公有方法:提供全局访问点,返回唯一实例。
    饿汉式
java">public class Singleton {// 在类加载时就创建实例private static final Singleton INSTANCE = new Singleton();// 私有构造函数,防止外部通过 new 创建实例private Singleton() {}// 提供全局访问点public static Singleton getInstance() {return INSTANCE;}
}
  • 实例在类加载时立即创建。
  • 无论是否使用,实例都会提前初始化。

特点

  1. 线程安全:实例在类加载时创建,由 JVM 保证线程安全。
  2. 提前加载:无论是否使用,实例都会在类加载时创建,可能会占用资源。
  3. 实现简单:代码简洁,无需考虑多线程问题。

懒汉式

java">public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
  • 优点:线程安全。
  • 缺点:每次调用都加锁,性能较差。

双重检查(DCL)

java">public class Singleton {private static volatile Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}
  • 优点:线程安全且性能较好。
  • 缺点:实现稍复杂。

静态内部类

java">public class Singleton {private Singleton() {}private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
  • 优点:线程安全,延迟加载,实现简单。
  • 因为实例的创建由 JVM 在类加载时完成,且静态内部类的加载是线程安全的。
  • 实例在第一次调用 getInstance() 时创建。
  • 延迟加载(Lazy Initialization),只有在需要时才创建实例。

枚举

java">public enum Singleton {INSTANCE;public void doSomething() {// 业务逻辑}
}
  • 优点:线程安全,防止反射攻击,简洁。

二、单例模式在框架源码中的运用

Java 标准库 RunTime类

Runtime 类Runtime 类是典型的单例模式实现,用于管理应用程序的运行环境。

java">public class Runtime {private static final Runtime currentRuntime = new Runtime();private Runtime() {}public static Runtime getRuntime() {return currentRuntime;}
}
  • 通过 Runtime.getRuntime() 获取唯一的 Runtime 实例。

Spring Framework

Spring 框架中的单例模式主要用于管理 Bean 的生命周期。
Spring 的默认 Bean 作用域

  • Spring 容器中的 Bean 默认是单例的(Singleton 作用域),即每个 Spring 容器中只有一个实例。
java">@Service
public class UserService {// UserService 在 Spring 容器中是单例的
}@Service
@Scope("singleton") // 默认就是单例,可以省略
public class UserService {
}

单例 Bean 的管理

  • Spring 容器通过单例模式确保全局唯一的 Bean 实例,避免重复创建。
  • 示例:Spring 的 ApplicationContext 本身就是单例的。

三、总结

单例模式的优点

  1. 全局唯一访问点
    • 单例模式确保一个类只有一个实例,并提供一个全局访问点,方便其他模块或组件共享该实例。
    • 示例:配置管理器、日志记录器等。
  2. 节省资源
    • 对于创建成本高的对象(如数据库连接池、线程池),单例模式可以避免重复创建和销毁,节省系统资源。
    • 示例:数据库连接池通常只需要一个实例来管理所有连接。
  3. 保持一致性
    • 单例模式确保全局状态的一致性,避免多个实例导致状态冲突。
    • 示例:缓存管理器需要确保缓存数据的一致性。
  4. 简化设计
    • 单例模式可以减少对象创建的复杂性,使代码更简洁。
    • 示例:Spring 框架中的单例 Bean 管理。
  5. 线程安全(某些实现)
    • 通过正确的实现(如饿汉式、静态内部类、枚举),单例模式可以保证线程安全。

单例模式的缺点

  1. 难以扩展
    • 单例模式通常通过私有构造函数限制实例化,导致难以扩展或修改。
    • 如果需要多个实例或修改单例行为,可能需要重构代码。
  2. 隐藏依赖
    • 单例模式通过全局访问点获取实例,可能导致代码的依赖关系不清晰,增加调试和维护难度。
  3. 测试困难
    • 单例模式的全局状态可能导致单元测试困难,因为测试用例之间可能会相互影响。
    • 示例:测试一个依赖单例类的模块时,可能需要重置单例状态。
  4. 可能滥用
    • 单例模式容易被滥用,导致系统中出现大量全局状态,增加代码的耦合性。
  5. 生命周期管理问题
    • 单例模式的生命周期通常与应用程序一致,可能导致资源无法及时释放。
    • 示例:单例对象持有数据库连接,可能导致连接无法关闭。

单例模式的适用场景

  1. 全局唯一对象
    • 需要全局唯一的对象时,可以使用单例模式
    • 示例:
      • 配置管理器(ConfigManager)。
      • 日志记录器(Logger)。
      • 数据库连接池(DataSource)。
  2. 资源密集型对象
    • 对于创建成本高的对象,单例模式可以避免重复创建和销毁。
    • 示例:
      • 线程池(ThreadPool)。
      • 缓存管理器(CacheManager)。
  3. 共享状态管理
    • 需要全局共享状态时,可以使用单例模式
    • 示例:
      • 计数器(Counter)。
      • 全局锁(Lock)。
  4. 工具类
    • 对于无状态的工具类,单例模式可以避免重复创建实例。
    • 示例:
      • 日期格式化工具(DateUtils)。
      • 字符串处理工具(StringUtils)。
  5. 框架核心组件
    • 在框架中,单例模式常用于管理核心组件。
    • 示例:
      • Spring 的 ApplicationContext
      • MyBatis 的 SqlSessionFactory

http://www.ppmy.cn/news/1574324.html

相关文章

分布式与集群,二者区别是什么?

??分布式 分布式系统是由多个独立的计算机节点组成的系统,这些节点通过网络协作完成任务。每个节点都有自己的独立计算能力和存储能力,可以独立运行。分布式系统的目标是提高系统的可靠性、可扩展性和性能。 分布式服务包含的技术和理论 负载均衡&am…

游戏引擎学习第115天

仓库:https://gitee.com/mrxiao_com/2d_game_3 打开程序,查看我们在性能方面的进展 这段内容主要介绍了优化代码以利用处理器中的SIMD(单指令多数据)向量单元的基本概念。具体流程如下: 讲解了SIMD的基本原理,如何通…

【QT 网络编程】HTTP协议(二)

文章目录 🌟1.概述🌟2.代码结构概览🌟3.代码解析🌸Http_Api_Manager - API管理类🌸Http_Request_Manager- HTTP请求管理类🌸ThreadPool - 线程池🌸TestWindow- 测试类 🌟4.运行效果&…

c++ std::list使用笔记

c std::list使用笔记 1. 包含头文件2. 创建和初始化 std::list3. 添加元素4. 删除元素5. 访问元素6. 遍历 std::list7. 容量相关操作8. 其他常用操作9. 示例代码总结 std::list 是 C 标准库中的一个双向链表容器。与 std::vector 不同, std::list 不支持随机访问&…

go 网络编程 websocket gorilla/websocket

在 Go 语言中,你可以使用标准库中的 net/http 包和第三方库 gorilla/websocket 来实现一个 WebSocket 服务器。gorilla/websocket 库提供了对 WebSocket 协议的高级抽象,使得处理 WebSocket 连接变得相对简单。 package mainimport ("fmt"&qu…

科普:你的笔记本电脑中有三个IP:127.0.0.1、无线网 IP 和局域网 IP;两个域名:localhost和host.docker.internal

三个IP 你的笔记本电脑中有三个IP:127.0.0.1、无线网 IP 和局域网 IP。 在不同的场景下,需要选用不同的 IP 地址,如下为各自的特点及适用场景: 127.0.0.1(回环地址) 特点 127.0.0.1 是一个特殊的 IP 地…

Docker 替换到 Containerd (nerdctl相关指令)

因为docker不给用了,所以使用Containerd来代替 前置准备 安装 Containerd # 安装 containerd yum install -y yum-utils yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo yum install -y containerd.io # 生成默认配置文件 mkdir -p…

Docker-技术架构演进之路

目录 一、概述 常见概念 二、架构演进 1.单机架构 2.应用数据分离架构 3.应用服务集群架构 4.读写分离 / 主从分离架构 5.引入缓存 —— 冷热分离架构 6.垂直分库 7.业务拆分 —— 微服务 8.容器化引入——容器编排架构 三、尾声 一、概述 在进行技术学习过程中&am…