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

【多线程初阶】单例模式 指令重排序问题

文章目录

  • 1.单例模式
    • 1)饿汉模式
    • 2)懒汉模式
      • ①.单线程版本
      • ②.多线程版本
  • 2.分析单例模式里的线程安全问题
    • 1)饿汉模式
    • 2)懒汉模式
    • 懒汉模式是如何出现线程安全问题的
  • 3.解决问题
    • 进一步优化
      • 加锁导致的执行效率优化
      • 预防内存可见性问题
  • 4.解决指令重排序问题

1.单例模式

单例模式确保某个类在程序中只有一个实例,避免多次创建实例(禁止多次使用 new),要实现这一点的关键在于将类的所有构造方法声明为private,这样类外无法直接访问构造方法, new操作会编译时报错,从而保证类的实例的唯一性

只有一个实例,这样的要求,开发中是常见的需求场景,比如MySQL中的JDBC编程的第一步 DataSource(描述了数据库服务器在哪里,URL,user,password),这样的场景非常适合于作为单例,描述数据库的信息,类似于存储数据库信息这样的对象,由于数据库只有一份,即使搞多个这样的对象,也没啥意义,也是一样的信息

单例模式的实现方式主要有两种:饿汉方式 和 懒汉方式

1)饿汉模式

类加载的同时,创建实例

//通过饿汉模式构造单例模式class Singleton{private static  Singleton instance = new Singleton();public static  Singleton getInstance(){return instance;}private Singleton(){}}
public class Demo27 {public static void main(String[] args) {Singleton t1 = Singleton.getInstance();Singleton t2 = Singleton.getInstance();System.out.println(t1 == t2);// Singleton t3 = new Singleton();}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2)懒汉模式

  • 饿 是尽量早的创建实例
  • 懒 是尽量晚的创建实例(甚至可能不创建了) 延迟创建
  • 懒的另一个含义 高效率~~
  • 如果说,饿汉模式是在类加载的时候(一个比较早的时期),进行创建实例,并且使用private修饰所有的构造方法,使得代码中无法创建该类的其他实例
  • 那么懒汉方式的核心思路,就是延迟创建实例,真正用到实例,再去创建,甚至可能不创建实例,这样可以减小开销,提升效率

①.单线程版本

class  SingletonLazy{//懒汉模式-单线程private  static  SingletonLazy instance = null;public  static  SingletonLazy getInstance(){if(instance == null){instance = new SingletonLazy();}return instance;}private  SingletonLazy(){}
}

在这里插入图片描述

②.多线程版本

class  SingletonLazy{private  static  SingletonLazy instance = null;private  static  Object locker = new Object();public static  SingletonLazy getInstance(){if(instance == null){synchronized (locker){if (instance == null) {instance = new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}
public class Demo28 {public static void main(String[] args) {SingletonLazy s1 = SingletonLazy.getInstance();SingletonLazy s2 = SingletonLazy.getInstance();System.out.println(s1 == s2);}
}

在这里插入图片描述

2.分析单例模式里的线程安全问题

在这里插入图片描述

刚才编写的两份代码(饿汉和懒汉),是否是线程安全的?如果不安全如何解决问题?–这两个模式下的getInstance在多线程环境下调用,是否会出bug~

1)饿汉模式

在这里插入图片描述

2)懒汉模式

单线程版本的懒汉模式,如果在多线程环境下运行会出现什么问题?

在这里插入图片描述

在这里插入图片描述

  • instance 被 static 修饰说明这个一块内存,却被多个线程调用getInstance(),同一块空间被修改多次,并且该赋值操作不是原子的
  • getInstance()方法,不仅有读操作,还有写操作(满足if条件才赋值,不满足if条件不会赋值),所以判断条件和赋值这两个操作是密不可分的,就是因为无法保证这两步操作无法紧密执行,就会出现线程安全问题

懒汉模式是如何出现线程安全问题的

要了解这个问题,我们可以通过画时间轴来更直观的感受
在这里插入图片描述

  • ①.判断条件确实为NULL,OK那么线程1继续向下执行,不会跳出if代码块
  • ②.判断条件确实为NULL,OK那么线程2继续向下执行,不会跳出if代码块
  • ③.线程1可以继续向下执行,执行到new了一个对象,然后return
  • ④.线程2可以继续向下执行,执行到new了一个对象,然后return
  • 这里就出现了一个很大的问题 ! !

随着线程2的创建实例,这个操作覆盖掉了,线程1new出来的对象,线程1new出的对象被GC给释放掉了~~
第一次new这个对象的时候,是会进行加载数据的,有可能我们的数据达到100G,100G的数据从硬盘加载到内存 大概要十分钟
本来只需要十分钟,可以由于上述BUG,加载了两份,导致我们的启动时间逼近20分钟
这就与我们的预期不符,妥妥的BUG

3.解决问题

那我们如何解决这个线程安全问题呢? 常规方法:加锁!
在这里插入图片描述

  • 显然不行,不是加了synchronized就会线程安全的
  • 我们要具体的分析具体代码
  • 前面分析过了,是判断条件和赋值操作,这两个操作一起决定的代码在多线程环境下是非原子操作
  • 也就是说,修改是原子的,但是此处为"条件修改"
  • 我们希望,条件判断和修改打包成原子操作

在这里插入图片描述

  • 引入synchronized锁之后,后执行的线程就会在加锁的位置阻塞,阻塞到前一个线程解锁
  • 当后一个线程进入判断条件时,前一个线程已经修改完毕,instance不再为NULL,就不会执行后续的new操作
  • 后续再调用getInstance,此时都是直接执行 return
  • if + return 就是纯粹的读操作,读操作不涉及线程安全问题

进一步优化

加锁导致的执行效率优化

  • 虽然不涉及线程安全问题了,但是每次调用上述getInstance方法,都会触发一次加锁操作
  • 多线程情况下,这里的加锁,就是相互阻塞,影响程序的执行效率
  • 一旦阻塞,此时对于计算机来说,阻塞的时间间隔,就是"沧海桑田",不知道啥时候才能调度
  • 我们应该按需加锁,真正涉及到线程安全的时候,再加锁,不涉及,就不加
  • 加锁时机:若实例已经创建过了,就不涉及线程安全问题,没创建,就涉及线程安全问题

在这里插入图片描述
在这里插入图片描述

  • 以往都是"单线程"程序,单线程中.连续两个相同的 if ,是无意义的
  • 单线程中,执行流是只有一个的 ,上一个if的判断结果和下一个if的是一样的
  • 多线程中,两次判定之间,可能存在其他线程就把 if 中的 instance变量修改了 也导致这里的两次if的结论可能不同

预防内存可见性问题

  • 是否会存在内存可见性问题?
  • 可能会存在,编译器优化这个事情,非常复杂
  • 编译器优化往往不只是 javac自己的工作,通常是javac和jvm配合的效果(甚至是操作系统也要配合)
  • 为了稳妥起见,可以给instance直接加上一个volatile,从根本上杜绝,内存可见性问题
private  static volatile SingletonLazy instance = null;

4.解决指令重排序问题

  • 这里更关键的问题,是指令重排序导致的线程安全问题
  • 指令重排序也是编译器优化的一种体现形式,编译会在逻辑不变的前提下,调整代码的执行顺序,来达到提升性能的效果

举个栗子~~
比如,我们去买菜,我们买西红柿,鸡蛋,茄子,黄瓜
在这里插入图片描述

在这里插入图片描述
第二幅图的执行顺序,明显效率高多了

在这里插入图片描述

比如,我们生活中会出现的指令重排序情况

  • 1.买房 2. 装修 3.拿到钥匙
  • 1.买房 3.拿到钥匙 2.装修

在这里插入图片描述

  • volatile 的功能有两方面
  • 1.确保每次读取操作,都是读内存 -->确保内存可见性
  • 2.关于该变量的读取和修改操作,不会触发重排序 -->避免指令重排序问题

相关文章:

【多线程初阶】单例模式 指令重排序问题

文章目录 1.单例模式1)饿汉模式2)懒汉模式①.单线程版本②.多线程版本 2.分析单例模式里的线程安全问题1)饿汉模式2)懒汉模式懒汉模式是如何出现线程安全问题的 3.解决问题进一步优化加锁导致的执行效率优化预防内存可见性问题 4.解决指令重排序问题 1.单例模式 单例模式确保某…...

基于Python的气象数据分析及可视化研究

目录 一.🦁前言二.🦁开源代码与组件使用情况说明三.🦁核心功能1. ✅算法设计2. ✅PyEcharts库3. ✅Flask框架4. ✅爬虫5. ✅部署项目 四.🦁演示效果1. 管理员模块1.1 用户管理 2. 用户模块2.1 登录系统2.2 查看实时数据2.3 查看天…...

Pandas 可视化集成:数据科学家的高效绘图指南

为什么选择 Pandas 进行数据可视化? 在数据科学和分析领域,可视化是理解数据、发现模式和传达见解的关键步骤。Python 生态系统提供了多种可视化工具,如 Matplotlib、Seaborn、Plotly 等,但 Pandas 内置的可视化功能因其与数据结…...

新版NANO下载烧录过程

一、序言 搭建 Jetson 系列产品烧录系统的环境需要在电脑主机上安装 Ubuntu 系统。此处使用 18.04 LTS。 二、环境搭建 1、安装库 $ sudo apt-get install qemu-user-static$ sudo apt-get install python 搭建环境的过程需要这个应用库来将某些 NVIDIA 软件组件安装到 Je…...

Vue 实例的数据对象详解

Vue 实例的数据对象详解 在 Vue 中,数据对象是响应式系统的核心,也是组件状态的载体。理解数据对象的原理和使用方式是成为 Vue 专家的关键一步。我将从多个维度深入剖析 Vue 实例的数据对象。 一、数据对象的定义方式 1. Options API 中的定义 在 Options API 中,使用 …...

Axure Rp 11 安装、汉化、授权

Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接:https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...

中科院1区顶刊|IF14+:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点

中科院1区顶刊|IF14:多组学MR联合单细胞时空分析,锁定心血管代谢疾病的免疫治疗新靶点 当下,免疫与代谢性疾病的关联研究已成为生命科学领域的前沿热点。随着研究的深入,我们愈发清晰地认识到免疫系统与代谢系统之间存在着极为复…...

GB/T 43887-2024 核级柔性石墨板材检测

核级柔性石墨板材是指以可膨胀石墨为原料、未经改性和增强、用于核工业的核级柔性石墨板材。 GB/T 43887-2024核级柔性石墨板材检测检测指标: 测试项目 测试标准 外观 GB/T 43887 尺寸偏差 GB/T 43887 化学成分 GB/T 43887 密度偏差 GB/T 43887 拉伸强度…...

ffmpeg(三):处理原始数据命令

FFmpeg 可以直接处理原始音频和视频数据(Raw PCM、YUV 等),常见场景包括: 将原始 YUV 图像编码为 H.264 视频将 PCM 音频编码为 AAC 或 MP3对原始音视频数据进行封装(如封装为 MP4、TS) 处理原始 YUV 视频…...

标注工具核心架构分析——主窗口的图像显示

🏗️ 标注工具核心架构分析 📋 系统概述 主要有两个核心类,采用经典的 Scene-View 架构模式: 🎯 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 🔧 关键函数&…...

作为点的对象CenterNet论文阅读

摘要 检测器将图像中的物体表示为轴对齐的边界框。大多数成功的目标检测方法都会枚举几乎完整的潜在目标位置列表,并对每一个位置进行分类。这种做法既浪费又低效,并且需要额外的后处理。在本文中,我们采取了不同的方法。我们将物体建模为单…...

基于Java项目的Karate API测试

Karate 实现了可以只编写Feature 文件进行测试,但是对于熟悉Java语言的开发或是测试人员,可以通过编程方式集成 Karate 丰富的自动化和数据断言功能。 本篇快速介绍在Java Maven项目中编写和运行测试的示例。 创建Maven项目 最简单的创建项目的方式就是创建一个目录,里面…...

自定义线程池1.2

自定义线程池 1.2 1. 简介 上次我们实现了 1.1 版本,将线程池中的线程数量交给使用者决定,并且将线程的创建延迟到任务提交的时候,在本文中我们将对这个版本进行如下的优化: 在新建线程时交给线程一个任务。让线程在某种情况下…...

Spring事务传播机制有哪些?

导语: Spring事务传播机制是后端面试中的必考知识点,特别容易出现在“项目细节挖掘”阶段。面试官通过它来判断你是否真正理解事务控制的本质与异常传播机制。本文将从实战与源码角度出发,全面剖析Spring事务传播机制,帮助你答得有…...

使用ch340继电器完成随机断电测试

前言 如图所示是市面上常见的OTA压测继电器,通过ch340串口模块完成对继电器的分路控制,这里我编写了一个脚本方便对4路继电器的控制,可以设置开启时间,关闭时间,复位等功能 软件界面 在设备管理器查看串口号后&…...

基于谷歌ADK的 智能产品推荐系统(2): 模块功能详解

在我的上一篇博客:基于谷歌ADK的 智能产品推荐系统(1): 功能简介-CSDN博客 中我们介绍了个性化购物 Agent 项目,该项目展示了一个强大的框架,旨在模拟和实现在线购物环境中的智能导购。它不仅仅是一个简单的聊天机器人,更是一个集…...

VSCode 没有添加Windows右键菜单

关键字:VSCode;Windows右键菜单;注册表。 文章目录 前言一、工程环境二、配置流程1.右键文件打开2.右键文件夹打开3.右键空白处打开文件夹 三、测试总结 前言 安装 VSCode 时没有注意,实际使用的时候发现 VSCode 在 Windows 菜单栏…...

vxe-table vue 表格复选框多选数据,实现快捷键 Shift 批量选择功能

vxe-table vue 表格复选框多选数据&#xff0c;实现快捷键 Shift 批量选择功能 查看官网&#xff1a;https://vxetable.cn 效果 代码 通过 checkbox-config.isShift 启用批量选中,启用后按住快捷键和鼠标批量选取 <template><div><vxe-grid v-bind"gri…...

Android Framework预装traceroute执行文件到system/bin下

文章目录 Android SDK中寻找traceroute代码内置traceroute到SDK中traceroute参数说明-I 参数&#xff08;使用 ICMP Echo 请求&#xff09;-T 参数&#xff08;使用 TCP SYN 包&#xff09; 相关文章 Android SDK中寻找traceroute代码 设备使用的是Android 11&#xff0c;在/s…...

生信服务器 | 做生信为什么推荐使用Linux服务器?

原文链接&#xff1a;生信服务器 | 做生信为什么推荐使用Linux服务器&#xff1f; 一、 做生信为什么推荐使用服务器&#xff1f; 大家好&#xff0c;我是小杜。在做生信分析的同学&#xff0c;或是将接触学习生信分析的同学&#xff0c;<font style"color:rgb(53, 1…...

react-pdf(pdfjs-dist)如何兼容老浏览器(chrome 49)

之前都是使用react-pdf来渲染pdf文件&#xff0c;这次有个需求是要兼容xp环境&#xff0c;xp上chrome最高支持到49&#xff0c;虽然说iframe或者embed都可以实现预览pdf&#xff0c;但为了后续的定制化需求&#xff0c;还是需要使用js库来渲染。 chrome 49测试环境 能用的测试…...

RKNN开发环境搭建2-RKNN Model Zoo 环境搭建

目录 1.简介2.环境搭建2.1 启动 docker 环境2.2 安装依赖工具2.3 下载 RKNN Model Zoo2.4 RKNN模型转化2.5编译C++1.简介 RKNN Model Zoo基于 RKNPU SDK 工具链开发, 提供了目前主流算法的部署例程. 例程包含导出RKNN模型, 使用 Python API, CAPI 推理 RKNN 模型的流程.   本…...

AT模式下的全局锁冲突如何解决?

一、全局锁冲突解决方案 1. 业务层重试机制&#xff08;推荐方案&#xff09; Service public class OrderService {GlobalTransactionalRetryable(maxAttempts 3, backoff Backoff(delay 100))public void createOrder(OrderDTO order) {// 库存扣减&#xff08;自动加全…...

20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题

20250609在荣品的PRO-RK3566开发板的Android13下解决串口可以执行命令但是脚本执行命令异常的问题 2025/6/9 20:54 缘起&#xff0c;为了跨网段推流&#xff0c;千辛万苦配置好了网络参数。 但是命令iptables -t filter -F tetherctrl_FORWARD可以在调试串口/DEBUG口正确执行。…...

[QMT量化交易小白入门]-六十二、ETF轮动中简单的评分算法如何获取历史年化收益32.7%

本专栏主要是介绍QMT的基础用法,常见函数,写策略的方法,也会分享一些量化交易的思路,大概会写100篇左右。 QMT的相关资料较少,在使用过程中不断的摸索,遇到了一些问题,记录下来和大家一起沟通,共同进步。 文章目录 相关阅读1. 策略概述2. 趋势评分模块3 代码解析4 木头…...

21-Oracle 23 ai-Automatic SQL Plan Management(SPM)

小伙伴们&#xff0c;有没有迁移数据库完毕后或是突然某一天在同一个实例上同样的SQL&#xff0c; 性能不一样了、业务反馈卡顿、业务超时等各种匪夷所思的现状。 于是SPM定位开始&#xff0c;OCM考试中SPM必考。 其他的AWR、ASH、SQLHC、SQLT、SQL profile等换作下一个话题…...

性能优化中,多面体模型基本原理

1&#xff09;多面体编译技术是一种基于多面体模型的程序分析和优化技术&#xff0c;它将程序 中的语句实例、访问关系、依赖关系和调度等信息映射到多维空间中的几何对 象&#xff0c;通过对这些几何对象进行几何操作和线性代数计算来进行程序的分析和优 化。 其中&#xff0…...

【Zephyr 系列 16】构建 BLE + LoRa 协同通信系统:网关转发与混合调度实战

🧠关键词:Zephyr、BLE、LoRa、混合通信、事件驱动、网关中继、低功耗调度 📌面向读者:希望将 BLE 和 LoRa 结合应用于资产追踪、环境监测、远程数据采集等场景的开发者 📊篇幅预计:5300+ 字 🧭 背景与需求 在许多 IoT 项目中,单一通信方式往往难以兼顾近场数据采集…...

二维数组 行列混淆区分 js

二维数组定义 行 row&#xff1a;是“横着的一整行” 列 column&#xff1a;是“竖着的一整列” 在 JavaScript 里访问二维数组 grid[i][j] 表示 第i行第j列的元素 let grid [[1, 2, 3], // 第0行[4, 5, 6], // 第1行[7, 8, 9] // 第2行 ];// grid[i][j] 表示 第i行第j列的…...

HTML版英语学习系统

HTML版英语学习系统 这是一个完全免费、无需安装、功能完整的英语学习工具&#xff0c;使用HTML CSS JavaScript实现。 功能 文本朗读练习 - 输入英文文章&#xff0c;系统朗读帮助练习听力和发音&#xff0c;适合跟读练习&#xff0c;模仿学习&#xff1b;实时词典查询 - 双…...