多线程案例(1) - 单例模式
目录
单例模式
饿汉模式
懒汉模式
前言
多线程中有许多非常经典的设计模式(这就类似于围棋的棋谱),这是用来解决我们在开发中遇到很多 "经典场景",简单来说,设计模式就是一份模板,可以套用。
单例模式
顾名思义,就是一个程序只能含有一个实例,有的场景中,希望一个类只能有一个对象,例如 JDBC 中的 DataSourse 实例就只需要一个。虽说程序员可以在编写代码时只给类创建一个对象,但是人毕竟没有机器靠谱,所以大佬们就设计了一套模板,按照模板来写代码,就不会出大的差错。
实现单例模式的方法有两种:饿汉模式 和 懒汉模式。
饿汉模式
就是在类加载时,就创建实例。
class SingleDemo{//在类加载的时候创建private static SingleDemo instance = new SingleDemo();//设置为private是为了防止在其他类中 new 一个实例,这样就不是单例模式了private SingleDemo(){ }//既然不能在外面创建实例,我们就要提供一个方法来得到这个唯一的实例public static SingleDemo getInstance() {return instance;//读取操作}
}
懒汉模式
顾名思义,就是在用到实例的时候再创建实例,与饿汉模式相比,提高了代码的效率。
class SingleLazyDemo{private static SingleLazyDemo instance = null;//设置为private是为了防止在其他类中new一个实例private SingleLazyDemo(){ }public static SingleLazyDemo getSingleLazyDemo(){//第一次需要实例时,创建实例if(instance == null){//修改操作instance = new SingleLazyDemo();}return instance;//读取操作}
}
了解了什么是饿汉模式和懒汉模式,我有一个问题:上述两种写法,那种是线程安全的?
我在前几篇博客中提到过,如果多个线程,同时修改同一个变量,此时就可能出现线程安全问题,所以显而易见,饿汉模式是线程安全的,它的方法中只涉及到读取操作,而懒汉模式是线程不安全的,它的方法中涉及到读取和修改操作。画个图理解一下:
接下来,我们就来解决懒汉模式的线程安全问题。导致该模式出现线程安全的原因其实在图中已经体现出来了,这个一个非原子操作,针对这一问题,我们的解决方法就是加锁。
class SingleLazyDemo{private static SingleLazyDemo instance = null;//设置为private是为了防止在其他类中new一个实例private SingleLazyDemo(){ }public static SingleLazyDemo getSingleLazyDemo(){synchronized (SingleLazyDemo.class){//使修改操作变成原子操作if(instance == null){//第一次需要实例时,创建实例instance = new SingleLazyDemo();}}return instance;}
}
此时,虽然懒汉模式的线程安全问题基本得到了解决,但是一旦这么写,后续每次调用 getInstance,都需要先加锁,而加锁的开销是很大的,只要涉及加锁,那么该代码就基本与"高性能"无缘了。实际上,我们的实例化对象的操作(即修改操作) 只是出现在第一次调用 getInstance 的时候。
一旦对象被new出来了,后续的线程调用 getInstance 就没有必要加锁了,因为这时候只用读取操作,线程是安全的,所以我们还需要再添加一个条件:
class SingleLazyDemo{private static SingleLazyDemo instance = null;//设置为private是为了防止在其他类中new一个实例private SingleLazyDemo(){ }public static SingleLazyDemo getSingleLazyDemo(){if(instance == null){//判断是否线程安全,要不要加锁synchronized (SingleLazyDemo.class){//使修改操作变成原子操作if(instance == null){//判断是否实例化instance = new SingleLazyDemo();}}}return instance;}
}
注意:这两个 if 虽然都是判断 instance 是否为 null, 但是第一个 if 实际上是借此判断线程是否要加锁,如果为null,就说明需要执行修改操作,线程不安全,要加锁,如果不为null,说明线程只要执行读取操作,线程安全,不要加锁。而第二个 if 则是借此判断是否要实例化对象。
在经过上述修改后,此代码还有一个问题,这就涉及到了之前没细讲的指令重排序问题,该问题也是因为编译器优化导致的,编译器为了提高执行效率,可能会在逻辑顺序不变的情况下,调整原有代码的执行顺序。
比如:new 操作,可以分成三步:1. 申请内存空间 2. 在内存空间上构造对象 3. 把内存的地址赋值给 instance 引用。在单线程中,new 操作可以按照 1 2 3 执行,也可以按照 1 3 2 执行,但是在多线程中,1 3 2 这样执行就可能导致线程安全问题。
举个例子:当 t1 线程执行完 1 3 时,instance 就已经是非空了,这个时候 2 还没有执行,t2 线程就开始执行,因为这个时候 instance 非空,所以 t2 线程直接返回 instance,这个时候如果 t2 线程中的代码访问 Instance 中的属性和方法,那么就会出现BUG,因为 Instance 还没有构造对象。
这个问题就需要使用 volatile 关键字来修饰 Instance,这样就可以保证 Instance 在 new 的过程中不会出现指令重排序的现象,下面是最终的代码:
class SingleLazyDemo{private static volatile SingleLazyDemo instance = null;//设置为private是为了防止在其他类中new一个实例private SingleLazyDemo(){ }public static SingleLazyDemo getSingleLazyDemo(){if(instance == null){//判断是否加锁synchronized (SingleLazyDemo.class){//使修改操作变成原子操作if(instance == null){//判断是否实例化instance = new SingleLazyDemo();}}}return instance;}
}
相关文章:

多线程案例(1) - 单例模式
目录 单例模式 饿汉模式 懒汉模式 前言 多线程中有许多非常经典的设计模式(这就类似于围棋的棋谱),这是用来解决我们在开发中遇到很多 "经典场景",简单来说,设计模式就是一份模板,可以套用。…...
Arduino驱动TCS34725传感器(颜色传感器篇)
目录 1、传感器特性 2、硬件原理图 3、控制器和传感器连线图 4、驱动程序 TCS34725是一款低成本,高性价比的RGB全彩颜色识别传感器,传感器通过光学感应来识别物体的表面颜色。...

知识库网站如何搭建?需要注意这五个要点!
正因为知识库提供结构化知识库来记载信息和知识,便于团队沉淀经验、共享资源,形成完整的知识体系并持续进化,使得它成为当前企业发展新宠。 构建自己/团队的知识库是一个良好的习惯,可以提高工作和学习效率,以下是一…...

【UE虚幻引擎】UE源码版编译、Andorid配置、打包
首先是要下载源码版的UE,我这里下载的是5.2.1 首先要安装Git 在你准备放代码的文件夹下右键点击Git Bash Here 然后可以直接git clone https://github.com/EpicGames/UnrealEngine 不行的话可以直接去官方的Github上下载Zip压缩包后解压 运行里面的Setup.bat&a…...

树和二叉树的相关概念及结构
目录 1.树的概念及结构 1.1 树的概念 1.2 树的相关概念 1.3 树的表示 1.3.1 孩子兄弟表示法 1.3.2 双亲表示法 1.4 树的实际应用 2.二叉树的概念及结构 2.1 二叉树的概念 2.2 特殊的二叉树 2.3 二叉树的性质 2.4 二叉树的存储 2.4.1 顺序存储 2.4.2 链式存储 1.树…...

MySQL安装validate_password_policy插件
功能介绍 validate_password_policy 是插件用于验证密码强度的策略。该参数可以设定三种级别:0代表低,1代表中,2代表高。 validate_password_policy 主要影响密码的强度检查级别: 0/LOW:只检查密码长度。 1/MEDIUM&am…...

数据在内存中的存储——练习3
题目: 3.1 #include <stdio.h> int main() {char a -128;printf("%u\n",a);return 0; }3.2 #include <stdio.h> int main() {char a 128;printf("%u\n",a);return 0; }思路分析: 首先二者极其相似%u是无符号格式进行…...

web-案例
分页插件 登录 事务...

第一章 JAVA入门
文章目录 1.2 Java 的特点1.2.1 简单1.2.2 面向对象1.2.3 与平台无关① 平台与机器指令② C/C程序依赖平台③ Java 虚拟机与字节码1.2.4 多线程1.2.5 动态1.30安装 JDK1.3.1 平台简介0 Java SE②Java EE1.4 Java 程序的开发步骤②保存源文件1.5.2 编译1.8 Java之父-James Gosli…...

二叉树详解(求二叉树的结点个数、深度、第k层的个数、遍历等)
二叉树,是一种特殊的树,特点是树的度小于等于2(树的度是整个树的结点的度的最大值),由于该特性,构建二叉树的结点只有三个成员,结点的值和指向结点左、右子树的指针。 typedef int DateType; t…...

Apollo配置中心及Python连接
本文将会介绍如何启动Apollo,在Apollo中配置参数,以及如何使用Python连接Apollo. Apollo介绍 在文章Python之读取配置文件和文章Python之配置文件处理中,笔者分别介绍了如何使用Python来处理ini, yaml, conf等配置文件。这种配置方式比较方便…...
LuatOS-SOC接口文档(air780E)--audio - 多媒体音频
常量 常量 类型 解释 audio.PCM number PCM格式,即原始ADC数据 audio.MORE_DATA number audio.on回调函数传入参数的值,表示底层播放完一段数据,可以传入更多数据 audio.DONE number audio.on回调函数传入参数的值,表示…...

Golang gorm manytomany 多对多 更新、删除、替换
Delete 移除 只删除中间表的数据 删除原有的 var a Article1db.Preload("Tag1s").Take(&a, 1)fmt.Printf("%v", a) {1 k8s [{1 cloud []} {2 linux []}]}mysql> select * from article1; ------------ | id | title | ------------ | 1 | k8s …...
FPGA-结合协议时序实现UART收发器(四):串口驱动模块uart_drive、例化uart_rx、uart_tx
FPGA-结合协议时序实现UART收发器(四):串口驱动模块uart_drive、例化uart_rx、uart_tx 串口驱动模块uart_drive、例化uart_rx、uart_tx,功能实现 文章目录 FPGA-结合协议时序实现UART收发器(四)࿱…...

Transformers-Bert家族系列算法汇总
🤗 Transformers 提供 API 和工具,可轻松下载和训练最先进的预训练模型。使用预训练模型可以降低计算成本、碳足迹,并节省从头开始训练模型所需的时间和资源。这些模型支持不同形式的常见任务,例如: 📝 自…...

Vulnhub系列靶机---HarryPotter-Fawkes-哈利波特系列靶机-3
文章目录 信息收集主机发现端口扫描dirsearch扫描gobuster扫描 漏洞利用缓冲区溢出edb-debugger工具msf-pattern工具 docker容器内提权tcpdump流量分析容器外- sudo漏洞提权 靶机文档:HarryPotter: Fawkes 下载地址:Download (Mirror) 难易程度ÿ…...

【服务器】ASUS ESC4000-E11 安装系统
ASUS ESC4000-E11说明书 没找到 ASUS ESC4000-E11的说明书,下面是ESC4000A-E11的说明书: https://manualzz.com/doc/65032674/asus-esc4000a-e11-servers-and-workstation-user-manual 下载地址: https://www.manualslib.com/manual/231379…...

创建java文件 自动添加作者、时间等信息 – IDEA 技巧
2023 09 亲测 文章目录 效果修改位置配置信息 效果 每次创建文件的时候,自动加上作者、时间等信息 修改位置 打开:File —> Settings —> Editor —> File and Code Templates —> includes —> FileHeader 配置信息 /*** author : Java…...

第27章_瑞萨MCU零基础入门系列教程之freeRTOS实验
本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写,需要的同学可以在这里获取: https://item.taobao.com/item.htm?id728461040949 配套资料获取:https://renesas-docs.100ask.net 瑞萨MCU零基础入门系列教程汇总: ht…...

Java学习之--类和对象
💕粗缯大布裹生涯,腹有诗书气自华💕 作者:Mylvzi 文章主要内容:Java学习之--类和对象 类和对象 类的实例化: 1.什么叫做类的实例化 利用类创建一个具体的对象就叫做类的实例化! 当我们创建了…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

简易版抽奖活动的设计技术方案
1.前言 本技术方案旨在设计一套完整且可靠的抽奖活动逻辑,确保抽奖活动能够公平、公正、公开地进行,同时满足高并发访问、数据安全存储与高效处理等需求,为用户提供流畅的抽奖体验,助力业务顺利开展。本方案将涵盖抽奖活动的整体架构设计、核心流程逻辑、关键功能实现以及…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
Python如何给视频添加音频和字幕
在Python中,给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加,包括必要的代码示例和详细解释。 环境准备 在开始之前,需要安装以下Python库:…...

SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

多种风格导航菜单 HTML 实现(附源码)
下面我将为您展示 6 种不同风格的导航菜单实现,每种都包含完整 HTML、CSS 和 JavaScript 代码。 1. 简约水平导航栏 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport&qu…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

Mac下Android Studio扫描根目录卡死问题记录
环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中,提示一个依赖外部头文件的cpp源文件需要同步,点…...
LeetCode - 199. 二叉树的右视图
题目 199. 二叉树的右视图 - 力扣(LeetCode) 思路 右视图是指从树的右侧看,对于每一层,只能看到该层最右边的节点。实现思路是: 使用深度优先搜索(DFS)按照"根-右-左"的顺序遍历树记录每个节点的深度对于…...