【创建型设计模式】单例模式
【创建型设计模式】单例模式
这篇博客接下来几篇都将阐述设计模式相关内容。
接下来的顺序大概是:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。
一、什么是单例模式
单例模式是一种创建型设计模式,它保证一个类仅有一个实例,并提供一个全局访问点。
核心思想:
- 限制类的实例化次数,确保全局只有一个实例。
- 提供统一访问该实例的方法。
Client 为客户端,Singleton 是单例类,通过调用 Singleton.getInstance() 来获取实例对象。
二、单例模式的 6 种方法
(1) 饿汉模式
public class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
特点:
- 初始化时间: 类加载时即完成实例化。
- 访问效率: 调用
getInstance
时无需等待,性能高。
优点:
- 基于类加载机制,天然线程安全。
- 实现简单,适用于单例对象较少被频繁初始化的场景。
缺点:
- 无法延迟加载,如果实例从未被使用,会浪费内存。
(2) 懒汉模式(线程不安全)
public class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
特点:
- 初始化时间: 第一次调用时才实例化。
- 访问效率: 多线程环境下存在问题,可能生成多个实例。
优点: 延迟加载,节省资源。
缺点: 线程不安全,多个线程同时调用可能创建多个实例,导致单例失效。
(3) 懒汉模式(线程安全)
public class Singleton {private static Singleton instance;private Singleton() {}public static synchronized Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}
特点:
- 线程安全性: 使用
synchronized
关键字确保多线程下只生成一个实例。
优点: 在多线程环境中保证安全。
缺点: 同步锁导致性能下降,每次调用 getInstance
都需排队,影响效率。
(4) 双重检查模式(DCL)
public class Singleton {// 1. 声明 volatile 修饰的静态实例变量private static volatile Singleton instance;// 2. 私有化构造方法,防止外部直接实例化private Singleton() {}// 3. 提供对外获取实例的静态方法public static Singleton getInstance() {// 第一次检查:避免不必要的同步if (instance == null) {synchronized (Singleton.class) { // 加锁// 第二次检查:确保实例未被其他线程创建if (instance == null) {instance = new Singleton();}}}return instance;}
}
特点:
- 性能优化: 减少同步开销。
- 线程安全: 使用
volatile
避免指令重排序,确保多线程环境下正确创建实例。
优点: 资源利用率高,延迟初始化,线程安全,性能优于懒汉模式(线程安全版)。
缺点: 实现复杂,某些情况下可能出现 DCL
失效问题(如旧版 JVM)。
DCL 模式的核心思想
DCL 的核心是两次检查和同步锁的结合。
**第一次检查(
if (instance == null)
):**在大多数情况下,实例已经被初始化,可以直接返回,不需要加锁。减少了不必要的同步,提高性能。**加锁(
synchronized
):**在实例尚未初始化时,进入临界区,防止多个线程同时创建实例。**第二次检查(
if (instance == null)
):**加锁后,再次检查实例是否为null
,防止多个线程同时通过第一次检查,确保单例对象只被创建一次。**
volatile
关键字:**确保instance
的修改对所有线程立即可见,防止指令重排序导致未完全初始化的对象被其他线程访问。
为什么需要
volatile
?问题:指令重排序
在没有
volatile
修饰时,JVM 编译器和 CPU 可能对以下代码进行优化:instance = new Singleton();
这一行代码可能被分解为以下三步:
- 为对象分配内存。
- 初始化对象。
- 将对象引用赋值给
instance
。但实际执行中,可能发生指令重排序:
- 为对象分配内存。
- 将对象引用赋值给
instance
。- 初始化对象。
如果线程 A 在步骤 2 后被切换,线程 B 进入并访问了
instance
,此时会得到一个未初始化的对象,导致程序行为不可预期。解决方法:
volatile
关键字禁止指令重排序,确保对象初始化完成后才将引用赋值给instance
。
(5) 静态内部类单例模式
public class Singleton {// 私有化构造方法,防止外部实例化private Singleton() {}// 静态内部类,持有单例实例private static class SingletonHolder {private static final Singleton INSTANCE = new Singleton();}// 提供全局访问点,返回静态内部类中的单例实例public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}
特点:
- 延迟加载: 第一次调用
getInstance
时才加载内部类,完成实例化。 - 线程安全: 内部类加载机制天然线程安全。
优点:
- 实现简单,避免同步开销。
- 兼具延迟加载和线程安全,推荐使用。
缺点: 无法控制实例销毁,适用单例生命周期较长的场景。
静态内部类的加载时机:
在 Java 中,类的加载是延迟的,只有在被真正使用时才会加载静态内部类。
主类被加载时,静态内部类不会被加载。
只有在调用静态内部类中的成员时,静态内部类才会被加载。
类加载的线程安全性:
JVM 在加载类时会自动保证线程安全。
静态变量
INSTANCE
只会被初始化一次。
(6) 枚举单例
public enum Singleton {INSTANCE; // 枚举单例的唯一实例// 单例类的其他方法public void doSomething() {System.out.println("Singleton instance is working!");}
}
特点:
- 线程安全: 枚举的线程安全由 JVM 保证。
- 防反序列化破坏: 默认枚举实例不可反序列化。
优点:
- 实现简单,天生线程安全。
- 避免反序列化和反射破坏单例。
缺点:
- 不支持延迟加载。
- 某些场景下(如需要灵活实例化控制)不适用。
public class Test {public static void main(String[] args) {Singleton instance = Singleton.INSTANCE;instance.doSomething();}
}
为什么枚举单例是线程安全的?
**JVM 保证:**枚举类型在类加载时会由 JVM 初始化,类加载过程是线程安全的。
**枚举的单一实例特性:**枚举中的每个实例在加载时被自动创建,并且不可被外部修改。
三、单例实现对比
实现方式 | 是否线程安全 | 是否延迟加载 | 性能 | 复杂度 |
---|---|---|---|---|
饿汉模式 | 是 | 否 | 高 | 简单 |
懒汉模式(不安全) | 否 | 是 | 高 | 简单 |
懒汉模式(同步) | 是 | 是 | 低(频繁加锁) | 简单 |
双重检查模式(DCL) | 是 | 是 | 较高 | 中等 |
静态内部类模式 | 是 | 是 | 高 | 简单 |
枚举单例 | 是 | 否 | 高 | 简单 |
相关文章:
【创建型设计模式】单例模式
【创建型设计模式】单例模式 这篇博客接下来几篇都将阐述设计模式相关内容。 接下来的顺序大概是:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。 一、什么是单例模式 单例模式是一种创建型设计模式,它保证一个类仅有一个实例&#…...
SpringBoot集成多个rabbitmq
1、pom文件 <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-amqp --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId><versio…...
JavaScript将至
JS是什么? 是一种运行在客户端(浏览器)的编程语言,实现人机交互效果 作用捏? 网页特效 (监听用户的一些行为让网页作出对应的反馈) 表单验证 (针对表单数据的合法性进行判断) 数据交互 (获取后台的数据, 渲染到前…...
Java中的线程池(如果想知道Java中有关线程池的知识,那么只看这一篇就足够了!)
前言:线程池是 Java 中用于高效管理并发任务的工具,通过复用线程、降低线程创建销毁的开销,提升系统性能与响应速度。它帮助开发者更好地控制线程生命周期和资源消耗,是高并发应用的重要组成部分。 ✨✨✨这里是秋刀鱼不做梦的BLO…...
Java 获取本机 IP 地址的方法
文章目录 一、使用 InetAddress.getLocalHost二、遍历网络接口获取 在 Java 编程中,若有本机的 IP 地址的需求,小编来展示一下方法: 一、使用 InetAddress.getLocalHost 一是最基本的获取本机 IP 地址的方式。 示例代码: impo…...
开源动态表单form-create-designer 扩展个性化配置的最佳实践教程
在开源低代码表单设计器 form-create-designer 的右侧配置面板里,field 映射规则为开发者提供了强大的工具去自定义和增强组件及表单配置的显示方式。通过这些规则,你可以简单而高效地调整配置项的展示,提升用户体验。 源码地址: Github | G…...
c语言数据结构与算法--简单实现线性表(顺序表+链表)的插入与删除
老规矩,点赞评论收藏关注!!! 目录 线性表 其特点是: 算法实现: 运行结果展示 链表 插入元素: 删除元素: 算法实现 运行结果 线性表是由n个数据元素组成的有限序列ÿ…...
5、AI测试辅助-生成测试用例思维导图
AI测试辅助-生成测试用例思维导图 创建测试用例两种方式1、Plantuml思维导图版本 (不推荐)2、Markdown思维导图版本(推荐) 创建测试用例两种方式 完整的测试用例通常需要包含以下的元素: 1、测试模块 2、测试标题 3、前置条件 4、…...
c ++零基础可视化——vector
c 零基础可视化——vector 初始化 vector<int> v0(5); // 0 0 0 0 0 vector<int> v1(5, 1); // 1 1 1 1 1 vector<int> v2{1, 2, 3} // 1 2 3 vector<int> v3(v1); // 1 1 1 1 1 vector<vector<int>> v4(2, vect…...
网络安全应急响应及其发展方向
计算机信息系统和网络已经成为社会重要的基础设施,而网络安全始终是笼罩在Internet天空的一片乌云,对社会的威胁也与日俱增。自1988年莫里斯蠕虫事件以来,网络安全事件逐年上升,所造成的损失越来越大。自从CERT/CC成立以来&#x…...
Linux应用编程(C语言编译过程)
目录 1. 举例 2.预处理 2.1 预处理命令 2.2 .i文件内容解读 3.编译 4.汇编 5.链接 5.1 链接方式 5.1.1 静态链接 5.1.2 动态链接 5.1.3 混合链接 1. 举例 Linux的C语言开发,一般选择GCC工具链进行编译,通过下面的例子来演示GCC如何使用&#…...
003 STM32基础、架构以及资料介绍——常识
注: 本笔记参考学习B站官方视频教程,免费公开交流,切莫商用。内容可能有误,具体以官方为准,也欢迎大家指出问题所在。 01什么是STM32(宏观) STM32属于一个微控制器,自带了各种常用通…...
输入三个整数x,y,z,请把这三个数由小到大输出。-多语言实现
目录 C 语言实现 Python 实现 Java 实现 Js 实现 题目:输入三个整数x,y,z,请把这三个数由小到大输出。 程序分析:我们想办法把最小的数放到x上,先将x与y进行比较,如果x>y则将x与y的值进行交换,然后…...
Java爬虫:获取商品详情的实践之旅
在当今这个信息爆炸的时代,数据的价值日益凸显。对于电商行业来说,商品详情的获取尤为重要,它不仅关系到产品的销售,还直接影响到用户体验。传统的人工获取方式耗时耗力,而自动化的爬虫技术则提供了一种高效解决方案。…...
vue 富文本图片如何拖拽
在Vue项目中实现富文本编辑器(如vue-quill-editor)的图片拖拽功能,需要结合Quill.js及其相关插件进行配置 安装必要的依赖包: 你需要安装vue-quill-editor作为富文本编辑器的基础组件。为了支持图片拖拽功能,你还需要…...
C#里怎么样实现自己的类满足for-each循环访问?
C#里怎么样实现自己的类满足for-each循环访问? 由于foreach语句使用起来比较方便,并且又简单,不像for循环那样需要写三个语句, 并且还有可能判断不对,导致少访问的情况。 在使用库里提供的容器时,都会有实现foreach的方式。 如果自己的类也实现这种方式,使用起来就比较…...
【Elasticsearch入门到落地】2、正向索引和倒排索引
接上篇《1、初识Elasticsearch》 上一篇我们学习了什么是Elasticsearch,以及Elastic stack(ELK)技术栈介绍。本篇我们来什么是正向索引和倒排索引,这是了解Elasticsearch底层架构的核心。 上一篇我们学习到,Elasticsearch的底层是由Lucene实…...
ODBC连接PostgreSQL数据库后,网卡DOWN后,客户端进程阻塞问题解决方法
问题现象:数据库客户端进程数据库连接成功后,再把跟数据库交互的网卡down掉,客户端进程就会阻塞,无法进行其他处理。该问题跟TCP keepalive机制有关。 可以在odbc.ini文件中增加相应的属性来解决,在odbc.ini 增加如下…...
Java Springboot河北任丘非遗传承宣传平台
一、作品包含 源码数据库设计文档万字PPT全套环境和工具资源部署教程 二、项目技术 前端技术:Html、Css、Js、Vue、Element-ui 数据库:MySQL 后端技术:Java、Spring Boot、MyBatis 三、运行环境 开发工具:IDEA/eclipse 数据…...
SpringBoot整合MQTT利用EMQX完成消息的发布与接收+Python模拟硬件测试通信
教程说明 本教程主要内容为使用SpringBoot整合MQTT利用EMQX代理服务完成MQTT的消息发送与接收,然后用Python模拟硬件与SpringBoot应用进行了MQTT消息的通信,教程详细,并在最后讲解了开发中的注意事项,本教程适用于物联网领域、Ja…...
【数据结构】【线性表】栈的基本概念(附c语言源码)
栈的基本概念 讲基本概念还是回到数据结构的三要素:逻辑结构,物理结构和数据运算。 从逻辑结构来讲,栈的各个数据元素之间是通过是一对一的线性连接,因此栈也是属于线性表的一种从物理结构来说,栈可以是顺序存储和顺…...
【jvm】为什么java是半编译半解释型语言
目录 1. 编译过程2. 解释过程3. 即时编译(JIT)过程4. 半编译半解释型语言的特点 1. 编译过程 1.Java源代码首先会被编译成字节码(Bytecode),这是一种与具体平台无关的中间代码。2.这一编译过程由Java编译器࿰…...
人工智能之机器学习5-回归算法2【培训机构学习笔记】
培训班ppt内容: 个人精进总结: 可解释方差 定义 可解释方差的回归评分函数是一种用于评估回归模型性能的指标,以下从其定义、计算公式、取值范围及意义、应用场景等方面进行详细介绍: 可解释方差(Explained Varian…...
C#动态类型详解:应用场景与注意事项
C#中的动态类型(dynamic)是C# 4.0引入的一个关键字,它允许在编译时绕过类型检查,将类型确定推迟到运行时。这意味着,当使用dynamic类型时,编译器不会对调用的方法或访问的属性进行类型检查,而是…...
C语言笔记(自定义类型:结构体、枚举、联合体 )
前言 本文对自定义类型的结构体创建、使用、结构体的存储方式和对齐方式,枚举的定义、使用方式以及联合体的定义、使用和存储方式展开叙述,如有错误,请各位指正。 目录 前言 1 结构体 1.1 结构体的声明 1.2 结构体的自引用 1.3 结构体变…...
【Golang】——Gin 框架与数据库集成详解
文章目录 1. 引言2. 初始化项目2.1 创建 Gin 项目2.2 安装依赖 3. 数据库驱动安装与配置3.1 配置数据库3.2 连接数据库3.3 在主函数中初始化数据库 4. 定义数据模型4.1 创建用户模型4.2 自动迁移 5. 使用 GORM 进行 CRUD 操作5.1 创建用户5.2 获取用户列表5.3 更新用户信息5.4 …...
nature communications论文 解读
题目《Transfer learning with graph neural networks for improved molecular property prediction in the multi-fidelity setting》 这篇文章主要讨论了如何在多保真数据环境(multi-fidelity setting)下,利用图神经网络(GNNs&…...
selinux及防火墙
selinux说明 SELinux 是 Security-Enhanced Linux 的缩写,意思是安全强化的 linux 。 SELinux 主要由美国国家安全局( NSA )开发,当初开发的目的是为了避免资源的误用。 httpd进程标签(/usr/share/nginx/html &#…...
实验二 系统响应及系统稳定性
实验目的 (1)学会运用Matlab 求解离散时间系统的零状态响应; (2)学会运用Matlab 求解离散时间系统的单位取样响应; (3)学会运用Matlab 求解离散时间系统的卷积和。 实验原理及实…...
【人工智能】深度学习入门:用TensorFlow实现多层感知器(MLP)模型
《Python OpenCV从菜鸟到高手》带你进入图像处理与计算机视觉的大门! 多层感知器(MLP)是一种基础的神经网络结构,广泛应用于分类和回归任务。作为深度学习的重要组成部分,理解并实现MLP是学习更复杂神经网络模型的基础。本文将介绍多层感知器的核心概念、数学原理,并使用…...
设计模式之建造者模式
建造者模式(Builder Pattern)是一种创建型设计模式,旨在将一个复杂对象的构建过程与其表示分离。它允许通过一步步地构造对象,而不需要暴露对象的内部细节和构建过程。通常,这个模式适用于创建对象时需要多个步骤&…...
谈一下开源生态对 AI人工智能大模型的促进作用
谈一下开源生态对 AI人工智能大模型的促进作用 作者:开源呼叫中心系统 FreeIPCC,Github地址:https://github.com/lihaiya/freeipcc 开源生态对大模型的促进作用是一个多维度且深远的话题,它不仅加速了技术创新的速度,…...
数据结构——树与二叉树
树 介绍 n个节点的有效集,它可为空树或非空树; 树是一种递归的结构。 对于非空树: 有且仅有一个称为根的节点。 除根节点以外其余节点可分为m个互不相交的有限集,且这些有限集本身也是一棵树,称为根的子树。 分等…...
神经网络(系统性学习三):多层感知机(MLP)
相关文章: 神经网络中常用的激活函数 神经网络(系统性学习一):入门篇 神经网络(系统性学习二):单层神经网络(感知机) 多层感知机(MLP) 多层感…...
Python学习——字符串操作方法
mystr “hello word goodbye” str “bye” Find函数:检测一个字符串中是否包含另一个字符串,找到了返回索引值,找不到了返回-1 print(mystr.find(str,0,len(mystr))) print(mystr.find(str,0,13)) index函数:检测一个字符串是否包含另一…...
css使用弹性盒,让每个子元素平均等分父元素的4/1大小
css使用弹性盒,让每个子元素平均等分父元素的4/1大小 原本: ul {padding: 0;width: 100%;background-color: rgb(74, 80, 62);display: flex;justify-content: space-between;flex-wrap: wrap;li {/* 每个占4/1 */overflow: hidden;background-color: r…...
C语言练习.if.else语句.strstr
今天在做题之前,先介绍一下,新学到的库函数strstr 想要使用它,要先给它一个头文件<string.h> char *strstr(const char*str1,const char*str2); 首先:1.strstr的返回值是char,字符类型的。 2.两个实参ÿ…...
STM32编程小工具FlyMcu和STLINK Utility 《通俗易懂》破解
FlyMcu FlyMcu 模拟仿真软件是一款用于 STM32 芯片 ISP 串口烧录程序的专用工具,免费,且较为非常容易下手,好用便捷。 注意:STM32 芯片的 ISP 下载,只能使用串口1(USART1),对应的串口…...
拉格朗日乘子(Lagrange Multiplier)是数学分析中用于解决带有约束条件的优化问题的一种重要方法,特别是SVM
拉格朗日乘子(Lagrange Multiplier)是数学分析中用于解决带有约束条件的优化问题的一种重要方法,也称为拉格朗日乘数法。 例如之前博文写的2月7日 SVM&线性回归&逻辑回归在支持向量机(SVM)中,为了…...
计算机网络——数据链路层
计算机广域网如果采用多点连接的方式: 因为广域网的链路中带宽大,延迟大,很有可能发送碰撞导致数据错误 而且布局困难...
HTMLCSS:3D金字塔加载动画
效果演示 这段代码通过CSS3的3D变换和动画功能,创建了一个旋转的金字塔加载动画,每个侧面都有不同的颜色渐变,底部还有一个模糊的阴影效果,增加了视觉的立体感。 HTML <div class"pyramid-loader"><div cl…...
Charles抓包工具-笔记
摘要 概念: Charles是一款基于 HTTP 协议的代理服务器,通过成为电脑或者浏览器的代理,然后截取请求和请求结果来达到分析抓包的目的。 功能: Charles 是一个功能全面的抓包工具,适用于各种网络调试和优化场景。 它…...
【Github】如何使用Git将本地项目上传到Github
【Github】如何使用Git将本地项目上传到Github 写在最前面1. 注册Github账号2. 安装Git工具配置用户名和邮箱仅为当前项目配置(可选) 3. 创建Github仓库4. 获取仓库地址5. 本地操作(1)进入项目文件夹(2)克隆…...
Express编写中间件
中间件 注意上面的调用next()。调用此函数会调用应用程序中的下一个中间件函数。该next()函数不是Node.js或Express API的一部分,而是传递给中间件函数的第三个参数。该next()函数可以命名为任何东西,但按照惯例,它总是被命名为“next”。为避…...
【Spring Boot】# 使用@Scheduled注解无法执行定时任务
1. 前言 在 Spring Boot中,使用Scheduled注解来定义定时任务时,定时任务不执行;或未在规定时间执行。 import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component;Component public c…...
Java基于SpringBoot+Vue的藏区特产销售平台
博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...
_computed _destinations() 为什么模板不写()
_computed _destinations() 为什么模板不写() 在 Vue 模板中使用计算属性时,不需要在属性名称后面加上括号 ()。计算属性本质上是一个 getter 函数,但 Vue 会将其转换为一个响应式的属性。Vue 会自动调用这些计算属性并将其结果作为属性值使用。 计算属…...
接口上传视频和oss直传视频到阿里云组件
接口视频上传 <template><div class"component-upload-video"><el-uploadclass"avatar-uploader":action"uploadImgUrl":on-progress"uploadVideoProcess":on-success"handleUploadSuccess":limit"lim…...
SimbaSchema在数据云平台中的可观测性实践与应用
建立可观测性体系是很多数据部门的理想。从Logs、Metrics、Traces和Meta抓取系统状态,到建立数据模型和指标体系,全流程均通过数据来支持运维。建立可观测性体系具备其行业标准,即所有数据平台类基础设施理论上都应提供Infomation Schema可观…...
VB、VBS、VBA的区别及作用
VB、VBS 和 VBA 是三种与微软 Visual Basic 相关的编程语言或环境,它们在功能和用途上有所不同: # Visual Basic (VB) Visual Basic 是一种面向对象的编程语言,最初由微软公司开发。它是一种高级编程语言,旨在简化开发过程&…...