JavaEE 第8节 单例模式详解
目录
概念
饿汉模式
懒汉模式
懒汉模式在多线程环境下的优化
1.线程安全问题
2.效率问题
3.指令重排序导致的问题
1)为什么要进行指令重排序?
2)指令重排序在上述代码为什么会构成问题?
导读:
单例模式是一种设计模式。
简单来讲设计模式就类似于下棋的棋谱,在特定的场景下使用这种模式(固定套路),可以让程序达到一个不错的效果。设计模式也是和编程语言相关的,有些设计模式是在给一些语言的语法填坑,而有些语言又不太依赖设计模式。
设计模式适合具有一定编程经验之后再去主要学习,如果缺乏变成经验,难以理解,别人这么设计的好处。
概念
单例模式的概念很简单,顾名思义,既在一个线程中一个类只包含一个对应的实例化对象。
在多线程程序中,有些场景就是要求只能创建一个实例化对象。
比如JDBC的设置数据源:
一个数据库,对应的MySQL服务器只有一份,DataSoruce这个类就没有必要new多份。
当然JDBC这块知识不了太解没关系,主要是告诉你,单例模式在多线程程序中其实是非常重要的。
单例模式的写法有很多,这里介绍两个最常用、最主流的写法:饿汉模式与懒汉模式。
饿汉模式
饿汉的饿,其实突出的是实例的创建时间比较早,是在类被加载的时候就创建了(可以近似的理解为在程序启动时创建)
为SingleL类写一个单例模式,用饿汉模式:
class SingleL{private static SingleL singleL=new SingleL();//直接new一个public static SingleL getSingleL(){return singleL;}
}
懒汉模式
懒汉的懒,其实突出的是实例的创建时间比较晚。
这里的晚,指的是,程序在需要这个类的时候才去实例化它:
class SingleL{private static SingleL instance=null;//先置为空,要的时候才实例化public static SingleL getInstance(){if(instance==null){//没有创建,先创建,有就直接返回instance=new SingleL();}return instance;}}
懒汉模式有一个优点,就是效率高,在计算机中懒其实是一个褒义词,勤快反而是一个贬义词。
为什么这么说呢?
最典型的场景就是打开一个内存比较大的文档,为了有一个更好的用户体验,响应速度因该是越快越好的,如果程序加载很“勤快”(提前加载完所有文档内容),打开文档程序所需的时间势必会变长,用户体验感就会变差。
但是如果程序加载比较的“懒”(先只加载几页,之用户想要看那一页,在加载那一页),响应速度就变得快了,用户体验感也会不错。
懒汉模式在多线程环境下的优化
1.线程安全问题
刚才的懒汉模式的代码在多线程环境下,肯定会造成线程安全问题,因为程序中不仅对变量进行了修改,而且读取和修改操作不是原子性的。
class SingleL{private static Object lock=new Object();private static SingleL instance=null;//先置为空,要的时候才实例化public static SingleL getInstance(){synchronized(lock){/*注意读写操作都要放到同步块中*/if(instance==null){instance=new SingleL();}}return instance;//返回之加不加到同步块中都无所谓,因为线程安全问题已经解决}
}
2.效率问题
这个问题是由上面解决了线程安全问题诱发的新的问题。
public static SingleL getInstance(){synchronized(lock){/*注意读写操作都要放到同步块中*/if(instance==null){instance=new SingleL();}}return instance;//返回之加不加到同步块中都无所谓,因为线程安全问题已经解决}
假如说由多个线程都要调用getInstance()那么就很可能导致多次的上锁和解锁,因为每次都要去判断有没有创建这个单例对象,这是非常消耗时间的。
解决办法也很简单,就是在线程安全的情况下,再次判断instance是否为null:
class SingleL{private static Object lock=new Object();private static SingleL instance=null;//先置为空,要的时候才实例化public static SingleL getInstance(){if(instance==null){synchronized(lock){/*注意读写操作都要放到同步块中*/if(instance==null){instance=new SingleL();}}}return instance;//返回之加不加到同步块中都无所谓,因为线程安全问题已经解决}
}
这就极大避免了多次上锁的情况了,你细品,两个if(instance==null)都不是多余的!
3.指令重排序导致的问题
1)为什么要进行指令重排序?
指令重排序和内存可见性一样都是编译器为了优化程序而引入的。
假如说有1、2、3条指令。这三条指令如果顺序执行可能是不经济的。例如执行1指令的时候需要和某个其他的指令同时争抢某一个资源导致冲突,但是如果先执行2,然后执行1就可以避免这种情况发生。
再比如这个形象的例子,老妈让你出去菜市场买三样东西:葱、姜、蒜:
为了节省时间继续打游戏,当然先去姜蒜两个摊位把东西买了,然后最后去葱这个摊位买啊。
2)指令重排序在上述代码为什么会构成问题?
在优化后的代码中new SingleL在编译时,可以大致分解成三个指令:
1、给对象分配内存空间。
2、调用构造函数初始化对象
3、讲instance引用指向分配内存的空间
通过指令重排序后,可能先执行1,然后直接执行3,最后执行2。
这样就会出现一个不安全的时机,就是1、3都执行完了,但是2还没有执行,此时instance引用指向的是一个无效的内存,因为还没有初始化好对象。
然后我们回到代码中来,假如说有两个线程,他们都刚开始执行,单例对象还没有创建:
3)问题的解决办法
指令重排序和内存可见性问题解决方式是一样的,用volatile关键字修饰变量。
volatile的作用:
1、保证变量可见性:一个线程对volatile变量修改,另一个线程可以立马看到。
2、禁止指令重排序:防止编译器对volatile变量的读/写操作进行指令重排序。
优化后的代码:
class SingleL{private static Object lock=new Object();private static volatile SingleL instance=null;//先置为空,要的时候才实例化,最后volatile禁止指令重排序public static SingleL getInstance(){if(instance==null){/*在同步块中执行*/synchronized(lock){if(instance==null){instance=new SingleL();}}}return instance;}
}
相关文章:

JavaEE 第8节 单例模式详解
目录 概念 饿汉模式 懒汉模式 懒汉模式在多线程环境下的优化 1.线程安全问题 2.效率问题 3.指令重排序导致的问题 1)为什么要进行指令重排序? 2)指令重排序在上述代码为什么会构成问题? 导读: 单例模式是一种…...

OpenAI 发布 GPT-4o 模型安全评估报告:风险等级为“中等”|TodayAI
OpenAI 近日发布了最新的 GPT-4o 系统卡,这是一份研究文件,详细介绍了公司在推出其最新 AI 模型之前所进行的安全措施和风险评估。根据该评估报告,GPT-4o 的总体风险等级被评定为 “中等” 。 GPT-4o 于今年 5 月首次公开发布。在其发布之前…...
学习前端面试知识
2024-8-9 打卡第十天 学习视频链接 js延迟加载 延迟加载:等页面加载完成后再进行加载提高页面加载速度defer属性,同步加载,让脚本与文档同步解析,顺序执行,当文档解析完成再执行defer,执行完再执行脚本&…...

Leetcode JAVA刷刷站(9)回文数
一、题目概述 二、思路方向 在Java中,判断一个整数是否为回文数,可以通过将该整数转换为字符串,然后比较字符串与其反转后的字符串是否相同来实现。但这种方法在整数非常大时可能不太高效,因为它依赖于字符串操作。一个更高效的方…...
数据结构算法
⩕ 单调栈 1、概念 对于一个栈,维持其单调性,有两种情况,单调递增栈:由栈底到栈顶单调递增 单调递减栈:由栈底到栈顶单调递减 2、核心模板( 单调递增栈 ) stack<int> stk; void …...
WordPress个性化站点
这个信息爆炸的时代,拥有一个能够迅速传达信息、展示个性、并能够与世界互动的在线平台,已成为企业和个人的基本需求。WordPress以其无与伦比的易用性和强大的扩展性,成为了构建此类平台的首选工具。而LNMP是由Linux、Nginx、MySQL和PHP组成的…...

GESP C++ 2024年03月一级真题卷
一、单选题(每题 2 分,共 30 分) 第 1 题 C表达式 (3 - 2) * 3 5 的值是( )。 A. -13 B. 8 C. 2 D. 0 答案:B 解析:略 第 2 题 C 语句 cout << "5%2" << 5 % 2 执行后的输出是…...

Linux驱动开发基础(Hello驱动)
所学内容来自百问网 目录 1. 文件在内核中的表示 2. 打开字符设备节点时,内核中也有对应的struct file 3. 编写驱动程序步骤 4. 相关知识点 4.1 涉及函数解析 4.2 module_init/module_exit的实现 4.3 register_chrdev的内部实现 4.4 class_destroy/device_…...

centos7安装 ES集群 elasticsearch
这里写自定义目录标题 编写启动脚本 elasticsearch.sh启动可能报错:elasticsearch 7.10启动报错 bootstrap checks failed解决方法问题原因:注意 退出xshell,重新登录: 上面两个配置项改完后,ES启动用户(es 或root) **…...

互联网应用主流框架整合【Redis数据结构及常用命令】
在大部分情况下我们使用Redis只是执行一些简单的命令操作,通常无需区分是否是在一个连接池里的同一个链接去执行,如果需要执行多条命令,需要保证命令在同一个链接里完成,则采用SessionCallback接口操作即可 Redis数据结构-字符串…...
GORM 自动迁移与命名策略
在现代软件开发中,数据库结构的维护和迁移是常见的挑战之一。GORM,作为 Go 语言中强大的 ORM 库,提供了自动迁移功能,帮助开发者轻松地管理数据库表结构的变更。此外,GORM 还允许开发者通过命名策略(Naming…...
python社会科学问题研究的计算实验
实验十五:社会科学问题研究的计算实践 1.实验目标及要求 (1)掌握网络视角 (2)掌握社会网络基础内容 (3)掌握友谊悖论 2.实验主要内容 随机生成一次符合社会网络特征的网络,通过计…...
Element Plus 发布 2.8.0
功能特性 组件更新 [color-picker] alpha-slider a11y (#14245 by tolking)添加 mention 组件 (#17586 by Fuphoenixes)[tree-v2] 添加 scrollTo 方法 (#14050 by kaine0923)[drawer] 添加 append-to 属性 (#17761 by tolking)[table] tree children 添加严格检查 (#13519 by t…...
解释区块链技术的应用场景和优势-水文
区块链技术是一种去中心化的分布式账本技术,其应用场景和优势如下: 金融领域:区块链可以用于加密货币交易,提供安全的、去中心化的支付系统。它也可以用于股票、债券和其他金融交易的记录和结算,提高交易的透明度和效率…...
等保测评基础知识(一)
1、时间类: 网络安全法: 2017年6月1日等保2.0实施时间: 2019年12月1日密码法: 2020年1月1日个人信息保护法: 2021年11月1日,数据安全法实施时间: 2021年9月1日关键信息基础…...

股指期货套期保值中的展期管理有哪些?
在复杂的金融市场环境中,展期作为一种重要的风险管理工具,被广泛应用于期货交易中,特别是当投资者需要对长期资产进行套期保值时。展期的核心思想在于,通过连续替换高流动性的近月期货合约来替代流动性较差的远月合约,…...

如何通过参考文献找到原文
当只有参考文献想要获取原文时,通常会用到以下方法: 举例参考文献1. 杨忠华,周勃,宁宝宽,等.面向新能源产业的专业研究生研创能力培养实践探索——基于“政产学研用”融合驱动[J].高教学刊,2024,10(23):19-22.DOI:10.19980/j.CN23-1593/G4.2024.23.004…...

春秋云境 | SQL | CVE-2022-4230
目录 靶标介绍 开启靶场 wpscan漏洞介绍 查询数据库表名 查询表中字段名 查询字段下数据 靶标介绍 WP Statistics WordPress 插件13.2.9之前的版本不会转义参数,这可能允许经过身份验证的用户执行 SQL 注入攻击。默认情况下,具有管理选项功能 (ad…...

3.串口(UART)
串口理论部分可看51部分:链接 数据帧 帧头(2字节,例如AA、BB) 数据长度(2字节) 数据 CRC16校验(2字节) 帧尾(2字节) 代码编写 串口一发送命令控制LED灯(PB5、PE5) LED灯、串口、…...

macOS Sonoma 14.6.1 (23G93) Boot ISO 原版可引导镜像下载
macOS Sonoma 14.6.1 (23G93) Boot ISO 原版可引导镜像下载 2024 年 8 月 8 日凌晨,macOS Sonoma 14.6.1 发布,本更新包含了重要的错误修复,并解决了导致高级数据保护无法启用或停用的问题。同时带来了 macOS Ventura 13.6.9 安全更新。 本…...

SpringBoot-17-MyBatis动态SQL标签之常用标签
文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…...

Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)
CSI-2 协议详细解析 (一) 1. CSI-2层定义(CSI-2 Layer Definitions) 分层结构 :CSI-2协议分为6层: 物理层(PHY Layer) : 定义电气特性、时钟机制和传输介质(导线&#…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...

微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...

【Redis】笔记|第8节|大厂高并发缓存架构实战与优化
缓存架构 代码结构 代码详情 功能点: 多级缓存,先查本地缓存,再查Redis,最后才查数据库热点数据重建逻辑使用分布式锁,二次查询更新缓存采用读写锁提升性能采用Redis的发布订阅机制通知所有实例更新本地缓存适用读多…...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...