【多线程】单例模式 | 饿汉模式 | 懒汉模式 | 指令重排序问题
文章目录
- 单例模式
- 一、单例模式
- 1.饿汉模式
- 2.懒汉模式(单线程)
- 3.懒汉模式(多线程)
- 改进
- 4.指令重排序
- 1.概念
- 2.question:
- 3.解决方法
- 4总结:
单例模式
一、单例模式
单例,就是单个实例
在有些场景中,希望有的类只能有一个对象,通过代码的语法规范,达到编译器强制检查的效果
单例模式保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例
比如很多用来管理数据的对象,就是单个实例的。
1.饿汉模式
//希望有唯一实例
class Singleton {//单例模式private static Singleton instance = new Singleton();//因为是static成员,在Singleton这个类被加载的时候,这里就会创建实例public static Singleton getInstance(){//通过getInstance方法来获取到这个实例return instance;}private Singleton(){}//将构造方法设置为私有的}public static void main(String[] args) {Singleton instance1 = Singleton.getInstance();//通过类名调用成员方法,获取到唯一实例Singleton instance2 = Singleton.getInstance();System.out.println(instance1 == instance2);//true}
- 将构造方法设置为私有的,避免再次生成实例。只能通过getInstance()方法来获取类变量创建好的唯一实例
- 唯一实例是在类加载的时候创建的(创建时间早)->饿汉模式(比较急)
- 在饿汉模式中,如果在多线程中,多个线程同时读取同一个变量,是线程安全的。只读不修改。
2.懒汉模式(单线程)
比较从容,在第一次使用的时候,再去创建实例
class SingletonLazy{private static SingletonLazy instance = null;//先设置为空public static SingletonLazy getInstance(){if (instance == null){instance = new SingletonLazy();}return instance;}private SingletonLazy(){}}
-
在首次调用getInstance方法的时候,才会真正创建唯一实例
不调用就不会创建。
-
”懒“意味着高效,省略不必要的操作和开销,只在需要的时候才开始进行。

- 违背了单例的要求。
- 懒汉模式在多线程环境下,涉及到了同一个变量的读取和修改,就存在线程安全问题。
3.懒汉模式(多线程)
解决方法:对if的判断操作和创建实例操作进行加锁,使两个操作始终执行在一起。
class SingletonLazy{private static SingletonLazy instance = null;//先设置为空public static SingletonLazy getInstance(){synchronized(SingletonLazy.class){if (instance == null){instance = new SingletonLazy();}}return instance;}private SingletonLazy(){}
}
- 虽然进行了加锁,但是每次再调用getInstance()方法的时候,都会进行加锁操作。
- 而懒汉模式的线程安全问题,体现在第一次实例法创建。后续创建好实例后的所有调用操作,都是读操作,没有必要进行加锁。
- 加锁是一个开销很大的操作,加锁就可能涉及到锁竞争,一冲突就会引发堵塞等待,涉及线程的调度。
改进
在加锁操作前,再进行一次判断
如果实例未创建,此时存在线程安全问题,需要加锁。如果对象已经创建,此时线程就是安全的,不需要加锁
private static SingletonLazy instance = null;//先设置为空public static SingletonLazy getInstance() {if (instance == null) {//第一个if判断的是是否要加锁synchronized (SingletonLazy.class) {if (instance == null) {//第二个if判断的是,是否要new对象instance = new SingletonLazy();}}}return instance;}
首次拿到锁的那个进程,一定创建了这个唯一对象。等后续的进程拿到锁后,再次进行判断,就不会创建对象了。
指令重排序可能会对上述代码会产生影响。
4.指令重排序
1.概念
同样也是因为编译器的优化:
编译器为了执行效率,在保证逻辑不变的前提下,可能会调整原来代码的执行顺序,从而提高效率。
在单线程下安全,多线程下可能会存在误判。
在多线程下,创建实例的操作时,new操作可能会引发指令重排序问题
new操作分为三步:第一步一定是先执行的,可能是1 、2、3 或者1、3、2的顺序;来执行。
1.申请内存空间
2.在内存空间上构造对象(执行构造方法)
3.把内存的地址,赋值给instance引用
就类似于:买房 装修 交钥匙 / 买房、交钥匙、装修 最终效果是一样的。
假设线程1按照1、3、2的顺序来执行。当1和3执行完后,3直接进行赋值操作。此时,instance就不在是null了,而是指向的是一个还没有进行初始化的非法对象。此时1、3执行完,还没开始执行操作2,线程2就开始执行了 。线程2先对instance进行判断。此时intance==null 不成立,线程2直接返回instance。但是instance只是一个还没有进行初始化的非法对象。线程2获取的对象是不完全的。会产生严重的问题。
- 也就是说,由于操作指令的执行顺序被优化了,导致实例创建的不完全就被调用了。表面提前符合了判断标准,但是内部还没有进行完全。
- 就类似于:业主买的是精装房,直接交了钥匙,想要拎包入住时,发现没有进行装修还是毛坯房。实际上精装房是包含装修的。那么就是开发商的执行顺序出现了问题,没安排装修就交了钥匙~
2.question:
提问:既然线程1执行到new的过程中,就已经先加锁了。线程2还能new的1、3操作刚完时,就插进来执行吗?
因为线程2的第一个if没有涉及到加锁操作,是完全可以执行的。锁的阻塞等待,一定是两个线程都加锁的时候才会触发。线程1拿到锁时,修改了instance的引用。此时线程2并没有参与锁的竞争,只是进行了第一个if的判断,非空情况下也不会进入if内部进行加锁操作,而是直接进行了返回。因此没有涉及任何阻塞等待。
3.解决方法
采用volatile,用volatile来修饰instence,保证instence在修改的过程中,就不会进行指令重排序的现象了。
class SingletonLazy {private static volatile SingletonLazy instance = null;//先设置为空public static SingletonLazy getInstance() {if (instance == null) {//第一个if判断的是是否要加锁synchronized (SingletonLazy.class) {if (instance == null) {//第二个if判断的是,是否要new对象instance = new SingletonLazy();}}}return instance;}private SingletonLazy() {}
4总结:
单例模式三步走:
1.懒汉模式下的双重if嵌套
2.用Synchronized对第二个if和后续是实例化操作进行加锁
3.用volatile修饰实例,禁止指令重排序。
面试遇到的话,人生如戏全靠演技,要适当藏拙,一步一步优化。从单线程的懒汉模式,到加锁,再到指令重排序
点击移步博客主页,欢迎光临~

相关文章:
【多线程】单例模式 | 饿汉模式 | 懒汉模式 | 指令重排序问题
文章目录 单例模式一、单例模式1.饿汉模式2.懒汉模式(单线程)3.懒汉模式(多线程)改进 4.指令重排序1.概念2.question:3.解决方法4总结: 单例模式 一、单例模式 单例,就是单个实例 在有些场景中,…...
00_Qt概述以及如何创建一个QT新项目
Qt概述 1.Qt概述1.1 什么是Qt1.2 Qt的发展史1.3 支持的平台1.4 Qt版本1.5 Qt的下载与安装1.6 Qt的优点 2.QT新项目创建3.pro文件4.主函数5.代码命名规范和快捷键 1.Qt概述 1.1 什么是Qt Qt是一个跨平台的C图形用户界面应用程序框架。它为应用程序开发者提供建立艺术级图形界面…...
git报错
这里写自定义目录标题 git报错Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. 有一个原因就是在github上设置对应密钥时,有一个key获取应该设置为…...
【R: mlr3:超参数调优】
本次分享官网教程地址 https://mlr3book.mlr-org.com/chapters/chapter4/hyperparameter_optimization.html 型调优 当你对你的模型表现不满意时,你可能希望调高你的模型表现,可通过超参数调整或者尝试一个更加适合你的模型,本篇将介绍这些操…...
使用Pandas实现股票交易数据可视化
一、折线图:展现股价走势 1.1、简单版-股价走势图 # 简洁版import pandas as pdimport matplotlib.pyplot as plt# 读取CSV文件df pd.read_csv(../数据集/格力电器.csv)data df[[high, close]].plot()plt.show() 首先通过df[[high,close]]从df中获取最高价和收盘…...
蓝桥杯刷题-乌龟棋
312. 乌龟棋 - AcWing题库 /* 状态表示:f[b1,b2,b3,b4]表示所有第 i种卡片使用了 bi张的走法的最大分值。状态计算:将 f[b1,b2,b3,b4]表示的所有走法按最后一步选择哪张卡片分成四类:第 i类为最后一步选择第 i种卡片。比如 i2,则…...
美国纽扣电池认证标准要求16 CFR 第 1700和ANSI C18.3M标准
法规背景 为了纪念瑞茜哈姆史密斯(Reese Hamsmith)美国德州一名于2020年12月因误食遥控器里的纽扣电池而不幸死亡的18个月大的女婴。 美国国会于2022年8月16日颁布了H.R.5313法案(第117-171号公众法)也称为瑞茜法案(Reese’s Law)…...
华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理工具
文章目录 华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理工具1. 介绍2. 下载3. 静音模式、平衡模式、增强模式配置4. 配置电源方案与模式切换绑定5. 启动Ghelper控制面板6. 目前支持的设备型号 华硕ROG幻16笔记本电脑模式切换管理工具完美替代华硕奥创中心管理…...
【ROS2笔记六】ROS2中自定义接口
6.ROS2中自定义接口 文章目录 6.ROS2中自定义接口6.1接口常用的CLI6.2标准的接口形式6.3接口的数据类型6.4自定义接口Reference 在ROS2中接口interface是一种定义消息、服务或动作的规范,用于描述数据结构、字段和数据类型。ROS2中的接口可以分为以下的几种消息类型…...
设计模式-代理模式(Proxy)
1. 概念 代理模式(Proxy Pattern)是程序设计中的一种结构型设计模式。它为一个对象提供一个代理对象,并由代理对象控制对该对象的访问。 2. 原理结构图 抽象角色(Subject):这是一个接口或抽象类࿰…...
中伟视界:智慧矿山智能化预警平台功能详解
矿山智能预警平台是一种高度集成化的安全监控系统,它能够提供实时的监控和报警功能,帮助企业和机构有效预防和响应潜在的安全威胁。以下是矿山智能预警平台的一些关键特性介绍: 报警短视频生成: 平台能够在检测到报警时自动生成短…...
如何在PPT中获得网页般的互动效果
如何在PPT中获得网页般的互动效果 效果可以看视频 PPT中插入网页有互动效果 当然了,获得网页般的互动效果,最简单的方法就是在 PPT 中插入网页呀。 那么如何插入呢? 接下来为你讲解如何获得(此方法在 PowerPoint中行得通&#…...
HTML段落标签、换行标签、文本格式化标签与水平线标签
目录 HTML段落标签 HTML换行标签 HTML格式化标签 加粗标签 倾斜标签 删除线标签 下划线标签 HTML水平线标签 HTML段落标签 在网页中,要把文字有条理地显示出来,就需要将这些文字分段显示。在 HTML 标签中,<p>标签用于定义段落…...
NVIC简介
NVIC(Nested Vectored Interrupt Controller)是ARM处理器中用于中断管理的一个重要硬件模块。它负责处理来自多个中断源的中断请求,并根据中断的优先级来安排处理器执行相应的中断服务例程(ISR)。NVIC是ARM Cortex-M系…...
LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】
LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】 题目描述:解题思路一:解题思路二:0解题思路三:0 题目描述: 给出了一个由 n 个节点组成的网络,用 n n 个邻接矩阵图…...
【linux】yum 和 vim
yum 和 vim 1. Linux 软件包管理器 yum1.1 什么是软件包1.2 查看软件包1.3 如何安装软件1.4 如何卸载软件1.5 关于 rzsz 2. Linux编辑器-vim使用2.1 vim的基本概念2.2 vim的基本操作2.3 vim命令模式命令集2.4 vim底行模式命令集2.5 vim操作总结补充:vim下批量化注释…...
excel试题转word格式
序号试题选项答案 格式如上。输出后在做些适当调整就可以。 import pandas as pd from docx import Document from docx.shared import Inches# 读取Excel文件 df pd.read_excel(r"你的excel.xlsx")# 创建一个新的Word文档 doc Document()# 添加标题 doc.add_headi…...
C语言学习笔记之指针(二)
指针基础知识:C语言学习笔记之指针(一)-CSDN博客 目录 字符指针 代码分析 指针数组 数组指针 函数指针 代码分析(出自《C陷阱和缺陷》) 函数指针数组 指向函数指针数组的指针 回调函数 qsort() 字符指针 一…...
在Debian 12系统上安装Docker
Docker 在 Debian 12 上的安装 安装验证测试更多信息 引言 在现代的开发环境中,容器技术发挥着至关重要的作用。Docker 提供了快速、可靠和易于使用的容器化解决方案,使开发人员和 DevOps 专业人士能够以轻松的方式将应用程序从一个环境部署到另一个环…...
策略者模式(代码实践C++/Java/Python)————设计模式学习笔记
文章目录 1 设计目标2 Java2.1 涉及知识点2.2 实现2.2.1 实现两个接口飞行为和叫行为2.2.2 实现Duck抽象基类(把行为接口作为类成员)2.2.3 实现接口飞行为和叫行为的具体行为2.2.4 具体实现鸭子2.2.5 模型调用 3 C(用到了大量C2.0的知识&…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
Matlab | matlab常用命令总结
常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...
【论文阅读28】-CNN-BiLSTM-Attention-(2024)
本文把滑坡位移序列拆开、筛优质因子,再用 CNN-BiLSTM-Attention 来动态预测每个子序列,最后重构出总位移,预测效果超越传统模型。 文章目录 1 引言2 方法2.1 位移时间序列加性模型2.2 变分模态分解 (VMD) 具体步骤2.3.1 样本熵(S…...
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源文件需要同步,点…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
