当前位置: 首页 > news >正文

【多线程】单例模式 | 饿汉模式 | 懒汉模式 | 指令重排序问题

文章目录

  • 单例模式
    • 一、单例模式
      • 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修饰实例,禁止指令重排序。

面试遇到的话,人生如戏全靠演技,要适当藏拙,一步一步优化。从单线程的懒汉模式,到加锁,再到指令重排序

点击移步博客主页,欢迎光临~

偷cyk的图

相关文章:

【多线程】单例模式 | 饿汉模式 | 懒汉模式 | 指令重排序问题

文章目录 单例模式一、单例模式1.饿汉模式2.懒汉模式(单线程)3.懒汉模式(多线程)改进 4.指令重排序1.概念2.question:3.解决方法4总结: 单例模式 一、单例模式 单例,就是单个实例 在有些场景中&#xff0c…...

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&#xff09…...

华硕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):这是一个接口或抽象类&#xff0…...

中伟视界:智慧矿山智能化预警平台功能详解

矿山智能预警平台是一种高度集成化的安全监控系统,它能够提供实时的监控和报警功能,帮助企业和机构有效预防和响应潜在的安全威胁。以下是矿山智能预警平台的一些关键特性介绍: 报警短视频生成: 平台能够在检测到报警时自动生成短…...

如何在PPT中获得网页般的互动效果

如何在PPT中获得网页般的互动效果 效果可以看视频 PPT中插入网页有互动效果 当然了,获得网页般的互动效果,最简单的方法就是在 PPT 中插入网页呀。 那么如何插入呢? 接下来为你讲解如何获得(此方法在 PowerPoint中行得通&#…...

HTML段落标签、换行标签、文本格式化标签与水平线标签

目录 HTML段落标签 HTML换行标签 HTML格式化标签 加粗标签 倾斜标签 删除线标签 下划线标签 HTML水平线标签 HTML段落标签 在网页中&#xff0c;要把文字有条理地显示出来&#xff0c;就需要将这些文字分段显示。在 HTML 标签中&#xff0c;<p>标签用于定义段落…...

NVIC简介

NVIC&#xff08;Nested Vectored Interrupt Controller&#xff09;是ARM处理器中用于中断管理的一个重要硬件模块。它负责处理来自多个中断源的中断请求&#xff0c;并根据中断的优先级来安排处理器执行相应的中断服务例程&#xff08;ISR&#xff09;。NVIC是ARM Cortex-M系…...

LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】

LeetCode-924. 尽量减少恶意软件的传播【深度优先搜索 广度优先搜索 并查集 图 哈希表】 题目描述&#xff1a;解题思路一&#xff1a;解题思路二&#xff1a;0解题思路三&#xff1a;0 题目描述&#xff1a; 给出了一个由 n 个节点组成的网络&#xff0c;用 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操作总结补充&#xff1a;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语言学习笔记之指针(二)

指针基础知识&#xff1a;C语言学习笔记之指针&#xff08;一&#xff09;-CSDN博客 目录 字符指针 代码分析 指针数组 数组指针 函数指针 代码分析&#xff08;出自《C陷阱和缺陷》&#xff09; 函数指针数组 指向函数指针数组的指针 回调函数 qsort() 字符指针 一…...

在Debian 12系统上安装Docker

Docker 在 Debian 12 上的安装 安装验证测试更多信息 引言 在现代的开发环境中&#xff0c;容器技术发挥着至关重要的作用。Docker 提供了快速、可靠和易于使用的容器化解决方案&#xff0c;使开发人员和 DevOps 专业人士能够以轻松的方式将应用程序从一个环境部署到另一个环…...

策略者模式(代码实践C++/Java/Python)————设计模式学习笔记

文章目录 1 设计目标2 Java2.1 涉及知识点2.2 实现2.2.1 实现两个接口飞行为和叫行为2.2.2 实现Duck抽象基类&#xff08;把行为接口作为类成员&#xff09;2.2.3 实现接口飞行为和叫行为的具体行为2.2.4 具体实现鸭子2.2.5 模型调用 3 C&#xff08;用到了大量C2.0的知识&…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

多模态大语言模型arxiv论文略读(108)

CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文标题&#xff1a;CROME: Cross-Modal Adapters for Efficient Multimodal LLM ➡️ 论文作者&#xff1a;Sayna Ebrahimi, Sercan O. Arik, Tejas Nama, Tomas Pfister ➡️ 研究机构: Google Cloud AI Re…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...

【MATLAB代码】基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),附源代码|订阅专栏后可直接查看

文章所述的代码实现了基于最大相关熵准则(MCC)的三维鲁棒卡尔曼滤波算法(MCC-KF),针对传感器观测数据中存在的脉冲型异常噪声问题,通过非线性加权机制提升滤波器的抗干扰能力。代码通过对比传统KF与MCC-KF在含异常值场景下的表现,验证了后者在状态估计鲁棒性方面的显著优…...

mac:大模型系列测试

0 MAC 前几天经过学生优惠以及国补17K入手了mac studio,然后这两天亲自测试其模型行运用能力如何&#xff0c;是否支持微调、推理速度等能力。下面进入正文。 1 mac 与 unsloth 按照下面的进行安装以及测试&#xff0c;是可以跑通文章里面的代码。训练速度也是很快的。 注意…...