【多线程】单例模式 | 饿汉模式 | 懒汉模式 | 指令重排序问题
文章目录
- 单例模式
- 一、单例模式
- 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的知识&…...
HTML 语义化
目录 HTML 语义化HTML5 新特性HTML 语义化的好处语义化标签的使用场景最佳实践 HTML 语义化 HTML5 新特性 标准答案: 语义化标签: <header>:页头<nav>:导航<main>:主要内容<article>&#x…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
基于SpringBoot在线拍卖系统的设计和实现
摘 要 随着社会的发展,社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统,主要的模块包括管理员;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...
【Nginx】使用 Nginx+Lua 实现基于 IP 的访问频率限制
使用 NginxLua 实现基于 IP 的访问频率限制 在高并发场景下,限制某个 IP 的访问频率是非常重要的,可以有效防止恶意攻击或错误配置导致的服务宕机。以下是一个详细的实现方案,使用 Nginx 和 Lua 脚本结合 Redis 来实现基于 IP 的访问频率限制…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
uniapp 小程序 学习(一)
利用Hbuilder 创建项目 运行到内置浏览器看效果 下载微信小程序 安装到Hbuilder 下载地址 :开发者工具默认安装 设置服务端口号 在Hbuilder中设置微信小程序 配置 找到运行设置,将微信开发者工具放入到Hbuilder中, 打开后出现 如下 bug 解…...
如何配置一个sql server使得其它用户可以通过excel odbc获取数据
要让其他用户通过 Excel 使用 ODBC 连接到 SQL Server 获取数据,你需要完成以下配置步骤: ✅ 一、在 SQL Server 端配置(服务器设置) 1. 启用 TCP/IP 协议 打开 “SQL Server 配置管理器”。导航到:SQL Server 网络配…...
