Java二十三种设计模式-单例模式(1/23)
引言
在软件开发中,设计模式是一套被反复使用的、大家公认的、经过分类编目的代码设计经验的总结。单例模式作为其中一种创建型模式,确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的概念、实现方式、使用场景以及潜在的问题。
基础知识,java设计模式总体来说设计模式分为三大类:
(1)创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
(2)结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
(3)行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
第一部分:单例模式概述
1.1 单例模式定义
单例模式是一种常用的软件设计模式,其核心思想是确保一个类在任何情况下都只有一个实例,并且提供一个全局访问点来获取这个唯一的实例。这种模式在需要全局状态信息或者需要频繁创建和销毁实例会导致资源浪费的情况下非常有用。
为什么需要单例模式?
- 全局访问点:在某些情况下,我们需要一个全局的访问点来操作某些资源或状态,例如配置信息管理器或数据库连接池。
- 资源优化:避免创建多个实例导致的资源浪费,例如线程池或缓存。
- 控制状态:在多线程环境下,控制对共享资源的并发访问,确保数据的一致性。
1.2 单例模式的特点
懒汉式单例模式
懒汉式单例模式的核心特点是“按需实例化”,即只有在第一次调用getInstance()
方法时才会创建实例。
特点:
- 延迟加载:只有在真正需要使用实例时才进行加载,可以提高系统启动速度。
- 线程不安全:在多线程环境下,如果没有适当的同步措施,可能会导致多个实例被创建。
示例代码(不同步):
public class LazySingleton {private static LazySingleton instance;private LazySingleton() {}public static LazySingleton getInstance() {if (instance == null) {instance = new LazySingleton();}return instance;}
}
饿汉式单例模式
与懒汉式相对,饿汉式单例模式在类加载时就立即创建实例。
特点:
- 线程安全:由于实例在类加载时就已经创建,因此不存在多线程同步问题。
- 资源浪费:如果实例从未被使用,也会造成资源浪费。
示例代码:
public class EagerSingleton {private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() {}public static EagerSingleton getInstance() {return instance;}
}
线程安全和线程不安全的单例模式
- 线程安全的单例模式:通过同步机制确保在多线程环境下,
getInstance()
方法不会被多次调用,从而创建多个实例。 - 线程不安全的单例模式:没有采取同步措施,可能会在多线程环境下创建多个实例。
线程安全的实现示例(双重检查锁定):
public class ThreadSafeSingleton {private static volatile ThreadSafeSingleton instance;private ThreadSafeSingleton() {}public static ThreadSafeSingleton getInstance() {if (instance == null) {synchronized (ThreadSafeSingleton.class) {if (instance == null) {instance = new ThreadSafeSingleton();}}}return instance;}
}
在这部分内容中,我们介绍了单例模式的基本定义和两种主要的实现方式:懒汉式和饿汉式。接下来,我们将深入探讨单例模式的实现细节和使用场景。
第二部分:单例模式的实现
2.1 懒汉式单例模式
代码示例:
public class LazySingleton {// volatile关键字确保多线程环境下的内存可见性private static volatile LazySingleton instance;private LazySingleton() {// 防止通过反射攻击单例模式if (instance != null) {throw new IllegalStateException("Instance already exists!");}}public static LazySingleton getInstance() {// 双重检查锁定,减少不必要的同步if (instance == null) {synchronized (LazySingleton.class) {if (instance == null) {instance = new LazySingleton();}}}return instance;}
}
优点和缺点:
-
优点:
- 按需创建实例,节省资源。
- 简单易懂,实现容易。
-
缺点:
- 线程安全性需要额外处理,如示例中的双重检查锁定。
- 反射和序列化可能破坏单例模式。
2.2 饿汉式单例模式
代码示例:
public class EagerSingleton {// 静态初始化,类装载时就完成实例化private static final EagerSingleton instance = new EagerSingleton();private EagerSingleton() {// 防止通过反射攻击单例模式if (instance != null) {throw new IllegalStateException("Instance already exists!");}}public static EagerSingleton getInstance() {return instance;}
}
优点和缺点:
-
优点:
- 线程安全,无需额外同步。
- 实现简单。
-
缺点:
- 无论是否使用,类装载时就完成实例化,可能导致资源浪费。
- 可能影响类装载时间。
2.3 线程安全的单例模式
双重检查锁定(Double-Checked Locking)
-
代码示例:
public class DoubleCheckedLockingSingleton {private static volatile DoubleCheckedLockingSingleton instance;private DoubleCheckedLockingSingleton() {// 防止通过反射攻击单例模式if (instance != null) {throw new IllegalStateException("Instance already exists!");}}public static DoubleCheckedLockingSingleton getInstance() {if (instance == null) {synchronized (DoubleCheckedLockingSingleton.class) {if (instance == null) {instance = new DoubleCheckedLockingSingleton();}}}return instance;} }
-
解释: 双重检查锁定首先检查实例是否存在,如果不存在,则进入同步块再次检查。这样避免了每次调用
getInstance()
时都要进行同步,提高了性能。
静态内部类
-
代码示例:
public class StaticInnerClassSingleton {private StaticInnerClassSingleton() {// 防止通过反射攻击单例模式if (InstanceHelper.INSTANCE != null) {throw new IllegalStateException("Instance already exists!");}}private static class InstanceHelper {private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();}public static StaticInnerClassSingleton getInstance() {return InstanceHelper.INSTANCE;} }
-
解释: 静态内部类利用了类装载的机制来保证初始化实例时的线程安全。静态内部类只有在第一次使用时才会装载,此时可以完成对单例对象的初始化。
2.4 枚举实现单例模式
代码示例:
public enum EnumSingleton {// 实例化枚举INSTANCE;// 可以添加其他方法public void doSomething() {// ...}
}
优点:
- 自然实现单例: 枚举在Java语言中天然是单例的,每个枚举常量在JVM中只有一个实例。
- 线程安全: 枚举实例的创建是线程安全的。
- 防止反射和序列化攻击: 枚举类型本身可以防止反射和序列化攻击,因为枚举类型不允许通过反射创建枚举实例。
通过上述实现方式,我们可以看到单例模式的多样性和灵活性。每种实现方式都有其适用场景和优缺点,开发者需要根据实际情况选择最合适的实现方法。在下一部分中,我们将探讨单例模式的使用场景和潜在问题。
第三部分:单例模式的使用场景
单例模式因其确保唯一实例的特性,在软件系统中有多种应用场景。本节将讨论几个典型的使用案例。
3.1 数据库连接池
为什么数据库连接池适合使用单例模式?
数据库连接池管理着数据库连接的创建、使用和销毁过程,其目的是减少频繁创建和销毁连接的开销,提高资源利用率。
- 资源复用:单例模式确保连接池全局只有一个实例,所有需要数据库连接的部分都使用同一个池,避免了创建多个连接池带来的资源浪费。
- 统一管理:通过单例模式,可以集中管理数据库连接的生命周期,包括连接的创建、监控和销毁。
- 性能优化:数据库连接的创建是一个耗时的操作,单例模式使得连接池可以在系统启动时初始化,并在运行期间复用这些连接,从而提高系统性能。
3.2 配置管理器
讨论配置信息管理中使用单例模式的优势。
配置管理器负责存储和提供应用的配置信息,如数据库配置、服务地址等。
- 全局访问:单例模式提供了一个全局访问点,使得系统中所有需要配置信息的部分都能方便地获取到最新的配置数据。
- 数据一致性:确保所有部分使用的是同一个配置管理器实例,从而保证了配置数据的一致性。
- 简化配置更新:当配置信息发生变化时,只需更新单例配置管理器中的信息,所有依赖配置的地方都会获得更新后的数据。
3.3 硬件资源管理器
讨论硬件资源管理中使用单例模式的场景。
硬件资源管理器负责对打印机、扫描仪等硬件设备进行控制和访问管理。
- 设备控制:硬件设备通常希望被控制系统以单例模式管理,以避免多个实例同时发送指令造成冲突。
- 状态同步:单例模式可以确保所有对硬件状态的更改都是同步的,防止因多实例并发操作导致的状态不一致问题。
- 减少资源消耗:硬件资源通常有限,单例模式可以减少对这些资源的重复申请和占用。
在这些场景中,单例模式的应用可以带来诸多好处,包括资源优化、数据一致性保证以及简化的系统设计。然而,单例模式的使用也需慎重考虑,以避免其潜在的问题,如测试困难、扩展性限制等。在后续部分,我们将进一步探讨单例模式的潜在问题和最佳实践。
第四部分:单例模式的潜在问题
4.1 测试困难
单例模式可能会给单元测试带来一些挑战,因为它依赖于全局状态。
- 全局状态问题:单例模式提供了全局访问点,这可能导致测试之间的相互影响,使得测试结果不可预测。
- 模拟困难:在需要模拟或替换单例组件的行为时,由于其全局性,很难进行模拟(Mocking)。
- 解决方案:可以通过使用依赖注入、接口或者使用测试框架提供的特定技术来解决这些问题。
4.2 扩展性问题
随着系统的发展,单例模式可能会成为系统扩展的瓶颈。
- 紧耦合问题:单例模式可能使得系统组件之间存在紧耦合,这在需要扩展或修改单例组件时可能导致问题。
- 服务降级:在分布式系统中,单例模式可能不再适用,因为它无法很好地支持服务的横向扩展。
4.3 多线程环境问题
在多线程环境中使用单例模式时,需要特别注意线程安全问题。
- 竞态条件:如果多个线程同时访问单例实例的创建过程,而这个过程中没有适当的同步机制,可能会导致创建多个实例。
- 性能问题:过度的同步可能会降低系统性能,特别是在高并发场景下。
第五部分:单例模式与其他设计模式的比较
5.1 单例模式与工厂模式
单例模式和工厂模式都涉及到类的实例化,但它们的目的和使用场景不同。
- 单例模式:确保一个类只有一个实例,并提供一个全局访问点。
- 简单工厂模式:创建对象的接口,让子类决定实例化哪一个类。
5.2 单例模式与原型模式
原型模式提供了通过复制现有的实例来创建新实例的能力,与单例模式形成对比。
- 单例模式:关注于限制实例的数量,确保全局只有一个实例。
- 原型模式:关注于通过复制来快速创建新实例,适用于创建复杂对象。
第六部分:最佳实践和建议
6.1 何时使用单例模式
单例模式适用于管理共享资源,如配置信息、连接池等。
- 全局状态管理:当全局状态需要被整个应用共享时。
- 资源优化:当创建多个实例会导致资源浪费时。
6.2 避免滥用单例模式
滥用单例模式可能导致代码难以测试和维护。
- 避免全局状态:尽量减少依赖全局状态,因为它可能导致代码难以理解和测试。
- 替代方案:考虑使用依赖注入等技术来减少对单例模式的依赖。
6.3 替代方案
依赖注入是一种常用的替代单例模式的技术。
- 依赖注入:通过依赖注入框架,可以将对象的创建和管理交给框架来处理,从而减少对单例模式的依赖。
- 服务定位器模式:提供了一种查找服务的方式,可以作为单例模式的替代方案。
通过深入分析单例模式的潜在问题和最佳实践,我们可以更明智地决定何时以及如何使用单例模式。在实际开发中,我们应该根据具体需求和上下文来选择最合适的设计模式。
结语
单例模式是一种简单但强大的设计模式,正确使用可以带来诸多好处,但也需要谨慎处理以避免潜在问题。通过本文的深入分析,希望读者能够对单例模式有更全面的理解,并在实际开发中做出合理的设计选择。
相关文章:

Java二十三种设计模式-单例模式(1/23)
引言 在软件开发中,设计模式是一套被反复使用的、大家公认的、经过分类编目的代码设计经验的总结。单例模式作为其中一种创建型模式,确保一个类只有一个实例,并提供一个全局访问点。本文将深入探讨单例模式的概念、实现方式、使用场景以及潜…...

Unity动画系统(3)---融合树
6.1 动画系统基础2-6_哔哩哔哩_bilibili Animator类 using System.Collections; using System.Collections.Generic; using UnityEngine; public class EthanController : MonoBehaviour { private Animator ani; private void Awake() { ani GetComponen…...
sqlalchemy.orm中validates对两个字段进行联合校验
版本 sqlalchemy1.4.37 需求说明 有个场景,需要在orm中对两个字段进行联合校验,当 col1 xxx’时,对 col2的长度进行检查,超过限制(500)时,进行截断。 网上找了很久,没找到类似的…...

【ROS2】高级:解锁 Fast DDS 中间件的潜力 [社区贡献]
目标:本教程将展示如何在 ROS 2 中使用 Fast DDS 的扩展配置功能。 教程级别:高级 时间:20 分钟 目录 背景 先决条件在同一个节点中混合同步和异步发布 创建具有发布者的节点创建包含配置文件的 XML 文件执行发布者节点创建一个包含订阅者的节…...

VirtualBox虚拟机与主机互传文件的方法
建立共享文件夹 1.点击设置,点击共享文件夹,添加共享文件夹路径,保存 2.启动虚拟机,点击设备,点击安装增强功能,界面会出现一个光碟图标,点击光碟图标 3.打开光碟图标,出现一个目…...

访问控制系列
目录 一、基本概念 1.客体与主体 2.引用监控器与引用验证机制 3.安全策略与安全模型 4.安全内核 5.可信计算基 二、访问矩阵 三、访问控制策略 1.主体属性 2.客体属性 3.授权者组成 4.访问控制粒度 5.主体、客体状态 6.历史记录和上下文环境 7.数据内容 8.决策…...

【BUG】已解决:ModuleNotFoundError: No module named ‘cv2’
已解决:ModuleNotFoundError: No module named ‘cv2’ 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页,我是博主英杰,211科班出身,就职于医疗科技公司,热衷分享知识,武汉城市开…...

成都亚恒丰创教育科技有限公司 【插画猴子:笔尖下的灵动世界】
在浩瀚的艺术海洋中,每一种创作形式都是人类情感与想象力的独特表达。而插画,作为这一广阔领域中的璀璨明珠,以其独特的视觉语言和丰富的叙事能力,构建了一个又一个令人遐想连篇的梦幻空间。成都亚恒丰创教育科技有限公司 在众多插…...

gite+picgo+typora打造个人免费笔记软件
文章目录 1️⃣个人笔记软件2️⃣ 配置教程2.1 使用软件2.2 node 环境配置2.3 软件安装2.4 gite仓库设置2.5 配置picgo2.6 测试检验2.7 github教程 🎡 完结撒花 1️⃣个人笔记软件 最近换了环境,没有之前的生产环境舒适,写笔记也没有劲头&…...

只用 CSS 能玩出什么花样?
在前端开发领域,CSS 不仅仅是一种样式语言,它更像是一位多才多艺的艺术家,能够创造出令人惊叹的视觉效果。本文将带你探索 CSS 的无限可能,从基本形状到动态动画,从几何艺术到仿生设计,只用 CSS 就能玩出令…...
Linux C++ 056-设计模式之迭代器模式
Linux C 056-设计模式之迭代器模式 本节关键字:Linux、C、设计模式、迭代器模式 相关库函数: 概念 迭代器模式(Iterator Pattern)是一种常用的设计模式。迭代器模式提供一种方法顺序访问一个聚合对象中的各个元素,而…...

【Elasticsearch7.11】reindex问题
参考博文链接 问题:reindex 时出现如下问题 原因:数据量大,kibana的问题 解决方法: 将DSL命令转化成CURL命令在服务上执行 CURL命令 自动转化 curl -XPOST "http://IP:PORT/_reindex" -H Content-Type: application…...

nginx代理缓存
在服务器架构中,反向代理服务器除了能够起到反向代理的作用之外,还可以缓存一些资源,加速客户端访问,nginx的ngx_http_proxy_module模块不仅包含了反向代理的功能还包含了缓存功能。 1、定义代理缓存规则 参数详解: p…...

[React 进阶系列] useSyncExternalStore hook
[React 进阶系列] useSyncExternalStore hook 前情提要,包括 yup 的实现在这里:yup 基础使用以及 jest 测试 简单的提一下,需要实现的功能是: yup schema 需要访问外部的 storage外部的 storage 是可变的React 内部也需要访问同…...
Linux C++ 055-设计模式之状态模式
Linux C 055-设计模式之状态模式 本节关键字:Linux、C、设计模式、状态模式 相关库函数: 概念 状态模式(State Pattern)是设计模式的一种,属于行为模式。允许一个对象在其内部状态改变时改变它的行为。对象看起来似…...

景联文科技构建高质量心理学系知识图谱,助力大模型成为心理学科专家
心理大模型正处于快速发展阶段,在临床应用、教育、研究等多个领域展现出巨大潜力。 心理学系知识图谱能够丰富心理大模型的认知能力,使其在处理心理学相关问题时更加精确、可靠和有洞察力。这对于提高心理健康服务的质量和效率、促进科学研究以及优化教育…...

【数学建模】——数学规划模型
目录 一、线性规划(Linear Programming) 1.1 线性规划的基本概念 1.2 线性规划的图解法 模型建立: 二、整数规划(Integer Programming) 2.1 整数规划的基本概念 2.2 整数规划的求解方法 三、非线性规划&#x…...

卸载linux 磁盘的内容,磁盘占满
Linux清理磁盘 https://www.cnblogs.com/siyunianhua/p/17981758 当前文件夹下,数量 ls -l | grep "^-" | wc -l ls -lR | grep "^-" | wc -l 找超过100M的大文件 find / -type f -size 100M -exec ls -lh {} \; df -Th /var/lib/docker 查找…...

LeetCode-随机链表的复制
. - 力扣(LeetCode) 本题思路: 首先注意到随机链表含有random的指针,这个random指针指向是随机的;先一个一个节点的拷贝,并且把拷贝的节点放在拷贝对象的后面,再让拷贝节点的next指向原链表拷贝…...

axios 下载大文件时,展示下载进度的组件封装——js技能提升
之前面试的时候,有遇到一个问题:就是下载大文件的时候,如何得知下载进度,当时的回复是没有处理过。。。 现在想到了。axios中本身就有一个下载进度的方法,可以直接拿来使用。 下面记录一下处理步骤: 参考…...

3.3.1_1 检错编码(奇偶校验码)
从这节课开始,我们会探讨数据链路层的差错控制功能,差错控制功能的主要目标是要发现并且解决一个帧内部的位错误,我们需要使用特殊的编码技术去发现帧内部的位错误,当我们发现位错误之后,通常来说有两种解决方案。第一…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...

汽车生产虚拟实训中的技能提升与生产优化
在制造业蓬勃发展的大背景下,虚拟教学实训宛如一颗璀璨的新星,正发挥着不可或缺且日益凸显的关键作用,源源不断地为企业的稳健前行与创新发展注入磅礴强大的动力。就以汽车制造企业这一极具代表性的行业主体为例,汽车生产线上各类…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
MySQL 索引底层结构揭秘:B-Tree 与 B+Tree 的区别与应用
文章目录 一、背景知识:什么是 B-Tree 和 BTree? B-Tree(平衡多路查找树) BTree(B-Tree 的变种) 二、结构对比:一张图看懂 三、为什么 MySQL InnoDB 选择 BTree? 1. 范围查询更快 2…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
MFE(微前端) Module Federation:Webpack.config.js文件中每个属性的含义解释
以Module Federation 插件详为例,Webpack.config.js它可能的配置和含义如下: 前言 Module Federation 的Webpack.config.js核心配置包括: name filename(定义应用标识) remotes(引用远程模块࿰…...

【堆垛策略】设计方法
堆垛策略的设计是积木堆叠系统的核心,直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法,涵盖基础规则、优化算法和容错机制: 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则: 大尺寸/重量积木在下…...