《单例模式的深度解读:实现方式、破坏情况与利弊权衡》
单例模式
一、单例模式的定义
单例模式(Singleton Pattern)是一种常见的软件设计模式,确保一个类只有一个实例存在,并提供一个全局访问点来获取该实例。
二、单例模式的实现方式
1.懒汉式单例
public class LazySingleton {private static LazySingleton instance; // 静态变量存储唯一实例private LazySingleton() { // 私有构造函数,防止外部创建实例System.out.println("懒汉式单例构造函数被调用");}public static LazySingleton getInstance() { // 获取实例的静态方法if (instance == null) { // 如果实例尚未创建instance = new LazySingleton(); // 创建实例System.out.println("懒汉式单例实例首次创建");}return instance; // 返回实例}
}
在懒汉式中,只有在第一次调用 getInstance 方法时才创建实例。
2.饿汉式单例
public class EagerSingleton {private static final EagerSingleton instance = new EagerSingleton(); // 直接创建并初始化唯一实例private EagerSingleton() { // 私有构造函数System.out.println("饿汉式单例构造函数被调用");}public static EagerSingleton getInstance() { // 获取实例的静态方法System.out.println("饿汉式单例实例获取");return instance; }
}
饿汉式在类加载时就创建了实例。
3.双重检验锁(Double Check Lock,DCL)
private volatile static Singleton singleton; // 使用volatile修饰,保证线程可见性
private Singleton (){} // 私有构造函数,防止外部直接实例化/*** 获取单例实例的方法* @return 单例对象*/
public static Singleton getInstance() {if (singleton == null) { // 第一次检查,避免不必要的同步synchronized (Singleton.class) { // 同步锁,保证线程安全if (singleton == null) { // 第二次检查,确保在同步块内只创建一次实例singleton = new Singleton(); // 创建单例实例}}}return singleton; // 返回单例实例
}
DCL 模式的突出特点在于,它成功地在确保线程安全的基础上,于多线程环境中依然能够维持出色的性能表现。通过巧妙地进行两次 singleton 是否为 null 的判断,有效地规避了不必要的同步操作所带来的性能损耗。
4.静态内部类
private static class SingletonHolder { // 静态内部类private static final Singleton INSTANCE = new Singleton(); // 在内部类中创建单例实例
}
private Singleton (){} // 私有构造函数,防止外部直接实例化/*** 获取单例实例的方法* @return 单例对象*/
public static final Singleton getInstance() {return SingletonHolder.INSTANCE; // 直接返回静态内部类中的单例实例
}
它专门适用于静态域的场景。巧妙地借助了 Java 的类加载机制,仅在实际需要获取实例时才对静态内部类进行加载,从而顺利实现了延迟初始化的效果,同时也切实保障了线程的安全性。
5.枚举
public enum Singleton { // 定义枚举类型的单例INSTANCE; // 唯一的枚举值即单例实例
}
它能够自动支持序列化机制,并且从根本上杜绝了多次实例化的可能性,是一种简洁、高效且极为可靠的单例实现手段。
三、破坏单例的情况及解决方法
-
反射破坏单例
- 问题:通过反射可以绕过私有构造函数的限制创建新的实例。
- 解决:在构造函数中进行判断,如果已经存在实例,抛出异常。
/*** 四、破坏单例的情况及解决方法*//*** 反射破坏单例的解决示例* 问题:通过反射可以绕过私有构造函数的限制创建新的实例。* 解决:在构造函数中进行判断,如果已经存在实例,抛出异常。*/ public class SafeSingleton {private static SafeSingleton instance; // 静态变量存储唯一实例/*** 私有构造函数* 在构造函数中检查是否已有实例存在,若有则抛出运行时异常*/private SafeSingleton() {if (instance!= null) {throw new RuntimeException("单例模式,禁止通过反射创建多个实例!");}}/*** 获取单例实例的方法* 如果实例不存在则创建,存在则直接返回* @return 单例对象*/public static SafeSingleton getInstance() {if (instance == null) {instance = new SafeSingleton();}return instance;} }/*** 序列化和反序列化破坏单例的解决示例* 问题:序列化后再反序列化可能创建新的实例。* 解决:实现 readResolve 方法返回已有的实例。*/ public class SerializableSingleton implements Serializable {private static final SerializableSingleton instance = new SerializableSingleton(); // 初始化唯一实例/*** 私有构造函数*/private SerializableSingleton() {}/*** 获取单例实例的方法* @return 单例对象*/public static SerializableSingleton getInstance() {return instance;}/*** 在反序列化时调用,返回已有的实例* @return 已有的单例实例*/private Object readResolve() {return instance;} }/*** 克隆破坏单例的解决示例* 问题:若单例类实现了 Cloneable 接口,通过克隆可能创建新实例。* 解决:在 clone 方法中抛出异常或返回已有实例。*/ public class CloneableSingleton implements Cloneable {private static CloneableSingleton instance = new CloneableSingleton(); // 存储唯一实例/*** 私有构造函数*/private CloneableSingleton() {}/*** 获取单例实例的方法* @return 单例对象*/public static CloneableSingleton getInstance() {return instance;}/*** 重写 clone 方法,抛出不支持克隆的异常* @return 抛出异常,禁止克隆* @throws CloneNotSupportedException 不支持克隆异常*/@Overrideprotected Object clone() throws CloneNotSupportedException {throw new CloneNotSupportedException("单例模式,禁止克隆!");} }四、总结
适用场景:
单例模式适用于在任何情况下都绝对只需要一个实例的情况,例如ServletContext、ServletConfig、ApplicationContext、DBPool、ThreadPool等。优点:
- 在内存中仅存在一个实例,显著降低了内存开销。
- 能够有效避免对资源的多重占用,保证资源的合理分配和使用。
- 设定了全局访问点,从而实现了对访问的严格管控。
缺点:
- 没有提供接口,导致扩展较为困难。
- 若要对单例对象进行扩展,只能通过修改代码来实现,缺乏其他灵活的途径。
总的来说,单例模式在特定场景下能够发挥其优势,有效地管理资源和控制访问,但在扩展性方面存在一定的局限性。在实际应用中,需要根据具体需求权衡其利弊,选择是否使用单例模式。
相关文章:
《单例模式的深度解读:实现方式、破坏情况与利弊权衡》
单例模式 一、单例模式的定义 单例模式(Singleton Pattern)是一种常见的软件设计模式,确保一个类只有一个实例存在,并提供一个全局访问点来获取该实例。 二、单例模式的实现方式 1.懒汉式单例 public class LazySingle…...
010607电压源和电流源受控源
电源的理论部分 1.6电压源和电流源1.理想电压源: 1.6电压源和电流源 1.理想电压源: 其两端电压总能保持定值或一定的时间函数,其值与流过它的电流i无关的元件叫理想电压源。 电路符号:中间与导线直通的圆圈 电压源:…...
快乐数求解
编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为: 对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。如果这个过程 结果为 1,…...
运维高级内容--为端口做标记、制定调度规则
rs: yum install mod_ssl -y #安装mod_ssl模块 让rs支持https systemctl restart http lvs: cd /boot/ ls less config-5.14.0-427.13.1.el9_4.x86_64 ipvsadm -A -t 192.168.0.200:80 -s rr ipvsadm -a -t 192.168.0.200:80 -r 192.168.0.10:80 -g -w 1 #轮询调度一次…...
后端Web之HTTP协议基础介绍
目录 1.HTTP概念 2.HTTP请求协议 3.HTTP响应协议 4.HTTP协议解析 1.HTTP概念 HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。它是万维网数据通信的基础,允许将超…...
深入解析Nginx限流策略:如何高效控制访问频率
摘要:本文将详细介绍Nginx限流模块的使用方法,包括基于IP地址的限流、基于并发连接的限流以及如何应对突发流量。通过实际案例,帮助读者掌握Nginx限流策略,确保服务器在高并发场景下的稳定运行。 一、引言 在高并发场景下&#x…...
锂电池剩余寿命预测 | Matlab基于Transformer-GRU的锂电池剩余寿命预测
目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab基于Transformer-GRU的锂电池剩余寿命预测,Transformer结合门控循环单元。 Matlab基于Transformer-GRU的锂电池剩余寿命预测(单变量) 运行环境Matlab2023b及以上。 首先从…...
深入理解Spring的IOC容器与依赖注入
深入理解Spring的IOC容器与依赖注入 引言 Spring框架的核心功能之一就是它的IOC容器,它为开发人员提供了强大的依赖管理和控制反转的能力。本文将详细介绍Spring的IOC容器以及依赖注入的基本概念和实现方式,并通过示例展示如何在实际项目中应用这些技术…...
Qt读写sysfs
本文介绍Qt读写sysfs。 在嵌入式Linux系统上开发Qt应用程序,经常会涉及到外设的控制,比如GPIO,PWM的控制,Linux环境下可以像操作文件一样操作它们,这通常会涉及到sysfs的读写。本文以读写GPIO为例,简要介绍…...
实景三维:解锁地理信息新维度,引领未来城市智慧之钥
在这个信息爆炸与科技日新月异的时代,地理信息与遥感技术正以前所未有的速度改变我们认知世界的方式。在推动“实景三维平台”这一前沿科技的构建上,它不仅是地理信息的立体呈现,更是智慧城市的基石,打开了通往未来城市规划、管理…...
汽车免拆诊断案例 | 2010款劳斯莱斯古斯特车中央信息显示屏提示传动系统故障
故障现象 一辆2010款劳斯莱斯古斯特车,搭载N74发动机,累计行驶里程约为11万km。车主反映,起动发动机后组合仪表和中央信息显示屏均提示传动系统故障。用故障检测仪检测,发现发动机控制模块2(DME2)中存储…...
监督学习和无监督学习是什么?
监督学习和无监督学习是机器学习中的两种基本学习方式,它们在处理数据和训练模型时有着显著的区别。 监督学习 定义: 监督学习是指利用一组已知类别的样本(即标记的数据)来调整分类器的参数,使其达到所要求性能的过程…...
YII2的errorHandler.errorAction失效原因
<?phpreturn [components => [errorHandler => [errorAction => site/error,],] ]; 这段配置存在错误,导致错误处理无法生效。为了解决这个问题,我们需要对配置进行优化。 代码查看:yii\web\ErrorHandler::renderException <?phpprotected function ren…...
已知p指向双向循环链表中的一个结点,其结点结构为data、prior、next三个域,写出算法change(p),交换p所指向的结点和它的前缀结点的顺序。
#include<assert.h> typedef struct SLnode {int data;struct SLnode* prior;struct SLnode* next; }SLnode,*SLnodelist; //创建结点 SLnode* createhead(int data) {SLnode* newnode (SLnode*)malloc(sizeof(SLnode));newnode->data data;newnode->next newno…...
什么是Tensor???为什么人工智能领域论文中经常出现这个名词
文章目录 什么是Tensor??数学符号表示 什么是Tensor?? Tensor,中文叫张量。Tensor实际上就是一个多维数组(multidimensional array)。 而Tensor的目的是能够创造更高维度的矩阵、向量。 数学符…...
爬虫练习_01
前言 基础爬虫小练习01 一、requests板块使用 demo_01 import requests from lxml import etreeurl "https://movie.douban.com/top250" headers {"authority": "movie.douban.com","method": "GET","path"…...
Datawhale X 魔搭 AI夏令营第四期 魔搭-AIGC方向 task02笔记
从零入门AI生图原理&实践 是 Datawhale 2024 年 AI 夏令营第四期的学习活动(“AIGC”方向),基于魔搭社区“可图Kolors-LoRA风格故事挑战赛”开展的实践学习。 Datawhale官方的Task2链接:Task02 往期Task1链接:Ta…...
多模态大语言模型的免训练视觉提示学习 ControlMLLM
ControlMLLM: Training-Free Visual Prompt Learning for Multimodal Large Language Models github paper 在本研究中,提出了一种无需进行训练的方法,通过可学习的潜变量优化将视觉提示注入到多模态大型语言模型(MLLMs)中。 在…...
Oracle|DM 常用|不常用 SQL大口袋
目录 一、前言 二、SQL写法 1、sql获取某一条数据中的前一条和后一条 2、实现like多个值的查询(Oracle和dm支持,MySQL未试过) 3、start with connect by prior 使用方法 4、用hextoraw解决select、update、delete语句执行慢 5、ORA-00…...
嵌入式软件--模电基础 DAY 1
C语言的学习告一段落了,要多多注意复习回顾,温故而知新,学习的过程就是与遗忘作斗争。接下来就是嵌入式学习中硬件电路方面的知识了。 一、电学基础 1.电流 电流(Current)是电荷在单位时间内通过导体横截面的流动量…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...
ubuntu搭建nfs服务centos挂载访问
在Ubuntu上设置NFS服务器 在Ubuntu上,你可以使用apt包管理器来安装NFS服务器。打开终端并运行: sudo apt update sudo apt install nfs-kernel-server创建共享目录 创建一个目录用于共享,例如/shared: sudo mkdir /shared sud…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
Nuxt.js 中的路由配置详解
Nuxt.js 通过其内置的路由系统简化了应用的路由配置,使得开发者可以轻松地管理页面导航和 URL 结构。路由配置主要涉及页面组件的组织、动态路由的设置以及路由元信息的配置。 自动路由生成 Nuxt.js 会根据 pages 目录下的文件结构自动生成路由配置。每个文件都会对…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
【WebSocket】SpringBoot项目中使用WebSocket
1. 导入坐标 如果springboot父工程没有加入websocket的起步依赖,添加它的坐标的时候需要带上版本号。 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId> </dep…...
恶补电源:1.电桥
一、元器件的选择 搜索并选择电桥,再multisim中选择FWB,就有各种型号的电桥: 电桥是用来干嘛的呢? 它是一个由四个二极管搭成的“桥梁”形状的电路,用来把交流电(AC)变成直流电(DC)。…...
