多线程之单例模式
前言
本篇介绍的是wait与notify方法,通过wait来顺序控制执行一些代码,了解单例模式,进行单例模式的简单实现,介绍饿汉模式下出现线程不安全的问题与解决;如有错误,请在评论区指正,让我们一起交流,共同进步!
文章目录
- 前言
- 1. 认识 wait 与 notify
- 1.1 wait,notify的使用
- 1.2 使用 wait 必须加锁
- 1.3 notify 也需要加锁
- 1.4 wait 与 sleep 的区别?
- 2.单例模式
- 2.1 单例模式的介绍
- 2.2 java语法中如何实现单例模式
- 2.3 实现单例模式
- 2.3.1 饿汉模式实现单例
- 2.3.2 懒汉模式实现单例模式
- 2.3.3 懒汉模式下产生的线程不安全
- 2.3.4 小结懒汉模式下产生的线程不安全
- 总结
本文开始
1. 认识 wait 与 notify
为什么要有wait 与 notify ?
线程调度是无序的,但是一定场景下,希望线程是有序执行的,这就可以使用wait 和 notify;
之前了解了,join能够等待,来控制顺序,但是其功效有限;而wait也是等待的作用,但又不相同;
1.1 wait,notify的使用
wait: 发现条件不满足 / 时机不成熟,就会阻塞等待;
notify: 其他线程构造了一个成熟的条件,就可以唤醒等待线程;
例如:
A去ATM取钱,A进去取钱,发现ATM中没有钱,A就会wait释放锁并阻塞等待,当另外的线程,把ATM冲上钱,就可以唤醒A,让A进行取钱操作;
wait与notify前提使用条件:
wait 和 notify 是Object 方法,只要是类对象,就可以使用wait,notify;
1.2 使用 wait 必须加锁
不加锁代码:
public static void main(String[] args) throws InterruptedException {Object object = new Object();object.wait();//中断就会报异常}
结果报错:
【注】illegal Monitor State Exception: 非法的,监视器(synchronized: 监视器锁),状态,异常;=》非法的锁状态异常;
object.wait() : wait方法做三件事
① 解锁: 这就说明,使用wait必须加锁(必须写的synchronized代码块中),才能解锁;
【注】加锁对象必须和wait的对象是同一个
② 阻塞等待
③ 当收到通知的时候,就唤醒,同时尝试重新获取锁;
join 与 wait 的区别:
前提有两个线程t1,t2;
① join等待一定是串行的,只能t2先执行完,再继续执行t1;
② wait和notify, 中的wait只是等待线程中的一部分;可以先让t2执行完一部分,再执行t1, t1执行一部分,t2再继续执行;(可以调整执行的顺序)
wait 使用的两种方式
使用wait会阻塞等待让线程进入WAITING状态;
① 带参数 的wait(时间):带参数的,指最大等待时间;(等到最大时间,没人通知,就会自己唤醒自己)
② 不带参数 的wait : 会一直等待;(只能等待被唤醒或者被中断)
1.3 notify 也需要加锁
前提:必须先执行wait, 然后才能有notify; (不然没锁,怎么解锁) 虽然没有唤醒wait,但是不会让代码出现异常;
notifyAll : 唤醒多个线程;
【注】如果有多个线程调用wait,notify只能随机唤醒一个;而notifyAll 可以把线程全部唤醒,此时多个线程重新竞争锁,再依次执行;
1.4 wait 与 sleep 的区别?
① wait用于线程通信,sleep用于阻塞线程;
② 设计的初心不同,wait解决线程之间顺序控制;sleep只能让当前线程休眠一会;
③ wait 是Object的方法,sleep是Thread的静态方法;
④ wait使用需要配合synchronized使用,sleep不需要;
2.单例模式
什么是单例模式?
单例模式,是一种经典的设计模式;类似于玩游戏的攻略 / 下棋的棋谱 ,有一些固定的招式;
2.1 单例模式的介绍
单例:指单个实例,一个程序中,某个类,只能创建出一个实例(对象),不能创建多个对象;
java中的单例模式,借助java语法,保证某个类,只能创建出一个实例,而不能new多次;
2.2 java语法中如何实现单例模式
这里介绍两种方式:
① 饿汉模式
从容的状态:比如吃饭洗碗,吃完饭马上洗碗;
② 懒汉模式
急迫的状态:比如吃饭洗碗,吃完饭,先不着急洗碗,等下一次用的再洗碗;
例如计算机读取硬盘中的文件内容,并显示;
饿汉:把文件所有内容全部读到内存中,并显示;
懒汉:只读取文件中的一部分,能填充当前屏幕,如果翻页,再读取其他文件内容,如果不翻页,就不用再读;
2.3 实现单例模式
此处把Singleton设置为单例的
2.3.1 饿汉模式实现单例
饿汉模式:开始直接创建实例,不管用不用;
【注】
① 唯一实例本体使用static修饰
② 获取实例方法提供get方法
③ 禁止外部再创建实例 使用private修饰无参构造(private修饰必须提供get方法来获取实例)
class Singleton {//唯一实例本体private static Singleton instance = new Singleton();//被static修饰,该属性是类的属性 =》 类对象//JVM 中,每个类的类对象只有唯一一份,类对象的成员也是唯一一份//private修饰,需要get方法,外部才能获取到//获取到实例的方法public static Singleton getInstance() {return instance;}//禁止外部new 实例private Singleton() {}
}
测试代码:
public static void main(String[] args) {//此时singleton1,singleton2调用的是同一个对象Singleton singleton1 = Singleton.getInstance();Singleton singleton2 = Singleton.getInstance();//为了防止new一个新的对象,给无参构造用private修饰//Singleton singleton3 = new Singleton();//此时不能new了}
2.3.2 懒汉模式实现单例模式
懒汉模式:开始不创建实例,等到使用的时候才会创建;
class SingletonLazy {//不马上创建实例private static SingletonLazy instance = null;public static SingletonLazy getInstance() {//等到调用的时候,才创建实例if(instance == null) {instance = new SingletonLazy();}//如果有了实例直接返回,也保证了只要一个实例return instance;}private SingletonLazy() {}
}
测试:
public static void main(String[] args) {SingletonLazy singletonLazy1 = SingletonLazy.getInstance();SingletonLazy singletonLazy2 = SingletonLazy.getInstance();//比较singletonLazy1 == singletonLazy2,相等就是同一个对象System.out.println(singletonLazy1 == singletonLazy2);}
2.3.3 懒汉模式下产生的线程不安全
通过上述两个方式实现单例模式,它们是线程安全的吗?
可以通过分析线程不安全产生的原因来判断;
饿汉模式:获取实例方法,只是读操作,不会修改变量,线程是安全的;
懒汉模式:多线程下,无法保证创建对象的唯一性, 线程是不安全的;
懒汉模式下两个线程调用getInstance:
问题1 产生原因:
t1线程先执行 if 判断,准备创建实例但还没有创建,此时t2 进行 if 判断,进入代码块也准备拆改那就实例,这就造成创建多个实例的现象;=》相当于多个线程修改同一个变量了;
( if 和 new不是原子的,会造成创建多个实例对象;创建过多会非常占内存空间,导致线程不安全;)
解决问题1方式:
加锁 保证 if 和 new 的原子性;
【注】加锁不一定线程安全,必须保证锁加对位置;
代码实现:
public static SingletonLazy getInstance() {//给当前类对象加锁synchronized (SingletonLazy.class) {if(instance == null) {instance = new SingletonLazy();}}return instance;}
问题2:
每次调用genInstance都会触发锁竞争,让加锁操作变得低效;
通过问题可以发现,懒汉模式的线程不安全,只出现在首次创建对象,一定对象创建完毕,后续调用getInstance,就只是读操作,就不会产生线程安全;=》只给第一次加锁即可;
解决问题2 方式:使用双重 if 解决
① 外层:判断是否要加锁
② 内层:判断是否创建对象
代码实现:
public static SingletonLazy getInstance() {//判断是否要加锁,如果有对象就不必加锁,此时是线程安全的//外层判断是否要加锁if(instance == null) {synchronized (SingletonLazy.class) {//等到调用的时候,才创建实例//里层判断是否创建对象if(instance == null) {instance = new SingletonLazy();}}}//如果有了实例直接返回,也保证了只要一个实例return instance;}
问题3:
多线程调用getInstance 进入操作会遇到new SingletonLazy操作, 创建实例会产生指令重排序问题;
创建实例一般有三步操作:
① 创建内存
② 调用构造方法 (初始化)
③ 把内存地址,赋给引用
原因:但是②③顺序不能够确定,假设两个线程t1,t2; t1可能先执行③顺序操作,此时t2执行 if判断,发现instance不为空,直接返回,而t1没有初始化变量等,t2只是拿到了不完整的实例对象,直接使用就会造成线程安全 ;
【注】上述指令重排序发生的概率很小,但不能忽视;
解决问题3 :使用volatile 禁止指令重排序
volatile private static SingletonLazy instance = null;
2.3.4 小结懒汉模式下产生的线程不安全
解决懒汉模式下的线程不安全方式:
① 加锁把 if 和 new操作变成原子的;
② 双重 if 减少不必要的加锁操作;
③ 使用 volatile 禁止指令重排序,保证后续线程能拿到完整对象;
总结
✨✨✨各位读友,本篇分享到内容如果对你有帮助给个👍赞鼓励一下吧!!
感谢每一位一起走到这的伙伴,我们可以一起交流进步!!!一起加油吧!!!
相关文章:
多线程之单例模式
前言 本篇介绍的是wait与notify方法,通过wait来顺序控制执行一些代码,了解单例模式,进行单例模式的简单实现,介绍饿汉模式下出现线程不安全的问题与解决;如有错误,请在评论区指正,让我们一起交…...
2023年绿色建筑国际会议(ICoGB 2023) | Springer独立出版
会议简介 Brief Introduction 2023年绿色建筑国际会议(ICoGB 2023) 会议时间:2023年5月21日-23日 召开地点:瑞典斯德哥尔摩 大会官网:www.icogb.org 2023年绿色建筑国际会议(ICoGB 2023)将围绕“绿色建筑”的最新研究领域而展开,为…...
Python中进程和线程到底有什么区别?
人生苦短,我用python python 安装包资料:点击此处跳转文末名片获取 一、进程和线程的关系 线程与进程的区别可以归纳为以下4点: 地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。…...
2023美赛春季赛F题思路数据代码论文分享
文章目录赛题思路赛题详情参赛建议(个人见解)选择队友及任务分配问题(重要程度:5星)2023美赛春季赛F题思路数据代码【最新】赛题思路 (赛题出来以后第一时间在CSDN分享) 最新进度在文章最下方卡片,加入获取…...
念一句咒语 AI 就帮我写一个应用,我人麻了...
原文链接:https://forum.laf.run/d/232 作为人类,我们时常会有自己独特的想法和脑洞大开的创意。然而,这些想法往往因为成本过高而无法实现,毕竟每个人的能力和精力都是有限的,尤其是对于程序员而言,不可能…...
开放平台设计之接口签名认证
前言 当前时代,数据是王道!当我们自己的平台有了足够大的数据量,就有可能诞生一个开放平台宫第三方分析、使用。那么我们怎么去实现对外部调用接口的控制与鉴权呢?这是我们今天的重点——接口签名认证!!&a…...
Vue自创插件发布到npm以及使用方法
Vue自创插件发布到npm以及使用方法 目标:创建my-popup-selector下拉框组件,并发布到npm,效果如下图: 禁用时样式: ①创建vue项目: my-popup-selector ②项目目录结构截图如下: ③在项目根目录…...
合成孔径雷达干涉测量InSAR数据处理、地形三维重建、形变信息提取、监测等实践技术
合成孔径雷达干涉测量(Interferometric Synthetic Aperture Radar, InSAR)技术作为一种新兴的主动式微波遥感技术,凭借其可以穿过大气层,全天时、全天候获取监测目标的形变信息等特性,已在地表形变监测、DEM生成、滑坡…...
Java刷题,蓝桥杯省赛第十二届(第一场)4-------------6
4、相乘题目本题总分:10 分【问题描述】小蓝发现,他将 1 至 1000000007 之间的不同的数与 2021 相乘后再求除以1000000007 的余数,会得到不同的数。小蓝想知道,能不能在 1 至 1000000007 之间找到一个数,与 2021 相乘后…...
Docker Cgroups——Docker 资源限制背后的技术原理
Docker Cgroups——Docker 资源限制背后的技术原理虽然在容器内部进程只能看到“掩饰”过的视图,但是在宿主机上,它就是一个普通的进程,与其他所有进程之间是平等竞争的关系。这就意味着虽然表面上被隔离了,但它实际上在与其他进程…...
十四. MySQL 锁相关
目录一. MySQL 锁基础Mysql 锁分类二. InnoDB 下的锁增删改查操作时底层的加锁处理表级锁1. 意向锁2. AUTO-INC锁id不连续对主从同步的影响3. 其它表锁行锁分析1. 记录锁 Record Locks2. 间隙锁 Gap Locks3. 临键锁 Next-Key Locks4. 插入意向锁5. 隐式锁6. 加锁算法InnoDB 行锁…...
ModStartBlog v7.0.0 网站简单统计,支持博客分享
ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用,支持后台一键快速安装,让开发者能快的实现业务功能开发。 系统完全开源,基于 Apache 2.0 开源协议。 功能特性 丰富的模块市场,后台一键快速安装 …...
【C语言蓝桥杯每日一题】—— 递增序列
【C语言蓝桥杯每日一题】—— 递增序列😎前言🙌递增序列🙌总结撒花💞😎博客昵称:博客小梦 😊最喜欢的座右铭:全神贯注的上吧!!! 😊作者…...
node_express框架01
01_express 基本结构 注意点:app.get 指定了 get 方法,如果是 app.all 就是指定了所有的请求方法(例如:post delete 都是包含的),而 app.get(/) 里面访问的是根路径,如果访问别的路径ÿ…...
想转行做程序员,该怎么选择开发语言?哪个岗位工资最高?
本文主要针对零基础想了解或者转行从事开发岗的同学。 我们收集了往届毕业同学和一些正在咨询的同学,发现大家在学习初期,对转行互联网做开发,最多的疑问或者顾虑大体分为几类: 现在哪门语言比较火? 学什么语言好找到工…...
JavaWeb——【笔记】3.2JavaWeb_Web核心_Request(请求)+Response(响应)
Request(请求)Response(响应)两个对象 request、response是service()方法中的两个参数。作用分别是获取请求数据进行逻辑处理;对数据解析设置响应数据 一、简介 示例: 二、Request(请求) 1、Request继承体系 能更清楚其是由谁创建及查阅什么文档 2、Request获…...
HTML 标签和属性
一些标签 单双标签 双标签。双标签指标签是成对出现的,也就是有一个开始标签和一个结束标签,开始标签用 <标签名> 表示,结束标签用 </标签名> 表示,只有一对标签一起使用才能表示一个具体的含义。例如 <html>&…...
MySQL 连接的使用
MySQL 连接的使用 在前几章节中,我们已经学会了如何在一张表中读取数据,这是相对简单的,但是在真正的应用中经常需要从多个数据表中读取数据。 本章节我们将向大家介绍如何使用 MySQL 的 JOIN 在两个或多个表中查询数据。 你可以在 SEL…...
配置案例丨EtherCAT转Profinet网关连接凯福科技总线步进驱动器
西门子S7-1200/1500系列的PLC,采用PROFINET实时以太网通讯协议,需要连接带EtherCAT的通讯功能的伺服驱动器等设备,就必须进行通讯协议转换。小疆GW-PN-ECATM系列的网关提供了,快速可行的解决方案。GW-PN-ECATM支持两种实时以太网通…...
VSCODE连接ssh服务器时提示could not establish connection to解决方法
VSCODE连接ssh服务器时提示could not establish connection to解决方法 1.点击扩展设置 在Remote.ssh:config file中输入config路径 重新连接即可,如果是之前连接过ubuntu现在无法连接则需要打开刚刚的地址文件中删掉known_hostsj即可 虚拟机中ubuntu安…...
嵌入式系统调试技术:从JTAG到多核同步的实战指南
1. 嵌入式系统调试技术概述在嵌入式系统开发过程中,调试环节往往占据整个开发周期的40%-60%时间。与通用计算机系统不同,嵌入式系统通常运行在资源受限的环境中,缺乏标准输入输出设备,这使得调试工作更具挑战性。我曾参与过多个工…...
Cadence 17.4 保姆级教程:从DRC检查到Gerber输出的完整避坑指南
Cadence 17.4 终极避坑指南:从DRC检查到Gerber输出的全流程实战 第一次使用Cadence Allegro 17.4导出Gerber文件时,那种如履薄冰的感觉至今记忆犹新。记得去年为TMC2300电机驱动模块导出生产文件时,因为一个简单的单位设置错误,导…...
加州自动驾驶测试报告解读:数据背后的技术演进与行业趋势
1. 从加州数据看自动驾驶的“成绩单”:2021年测试报告深度解读每年年初,自动驾驶圈子里不少人都会习惯性地去翻看一份来自美国加州的“成绩单”——加州机动车辆管理局发布的年度自动驾驶车辆测试报告。这份报告就像一份公开的“期中考试”排名ÿ…...
LangGraph、OpenClaw、Hermes:三种 Agent 路线,不是一回事
开头 这两年,只要聊到 Agent,绕不开三个名字:LangGraph、OpenClaw、Hermes。 它们都很火。 但也很容易被混在一起。 有人把 LangGraph 当成一个“Agent 产品”。 有人把 OpenClaw 当成一个“Agent 框架”。 也有人把 Hermes 理解成“另…...
硬件工程师实战指南:工业物联网安全、无线充电与TSN网络设计解析
1. 项目概述:一场面向硬件工程师的线上技术盛宴最近在整理行业资料时,翻到了EE Times几年前发布的一个“即将到来的线上技术活动”汇总页面。虽然发布时间是2018年,但里面提到的几个技术主题——工业物联网安全、硬件身份认证、工业以太网演进…...
Git 入门教程:从命令行到 IDE 集成
文章目录Git 入门教程:从命令行到 IDE 集成一、环境准备与初始配置1.1 安装 Git1.2 配置用户身份2.2 查看仓库状态2.3 添加文件到暂存区2.4 提交文件到本地仓库2.5 查看历史版本2.6 版本回退2.7 删除文件三、Git 分支操作(多人协作核心)3.1 分…...
Go+SQLite构建极简自托管笔记共享平台:从原理到部署实战
1. 项目概述:一个极简、自托管的笔记共享平台最近在折腾个人知识管理工具时,我一直在寻找一个能让我快速分享单篇笔记或代码片段,同时又不想依赖第三方云服务的方案。市面上的Pastebin类工具很多,但要么功能臃肿,要么隐…...
99%人开发Agent的致命误区!6大避坑指南助你从“调参怪”变“落地王”
本文揭示了开发Agent最常见的认知陷阱——将模型能力等同于系统能力,并提供了6大避坑指南:1. 掌握四层架构(Persona、CoT、Skill、MCP);2. 选择合适的执行模型(ReAct、Plan-and-Execute、Reflection&#x…...
【机器学习】Stacking模型融合:从原理到实战的进阶指南
1. 为什么需要Stacking模型融合? 当你用单一模型处理复杂数据时,经常会遇到这样的困境:线性回归对非线性关系束手无策,决策树容易过拟合,神经网络需要大量调参。我在去年参加Kaggle房价预测比赛时就深有体会——当时用…...
智能水表、血糖仪、工业HMI:STM32L152ZET6的超低功耗MCU应用版图
STM32L152ZET6:带LCD驱动的超低功耗Cortex-M3旗舰MCU 在电池供电的工业仪表、医疗设备和消费电子产品中,微控制器的功耗与集成度往往是决定产品可行性的关键因素。STM32L152ZET6是意法半导体STM32 L1系列中的高端型号,采用2020mm的LQFP-144封…...

