多线程之单例模式
前言
本篇介绍的是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安…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...
【碎碎念】宝可梦 Mesh GO : 基于MESH网络的口袋妖怪 宝可梦GO游戏自组网系统
目录 游戏说明《宝可梦 Mesh GO》 —— 局域宝可梦探索Pokmon GO 类游戏核心理念应用场景Mesh 特性 宝可梦玩法融合设计游戏构想要素1. 地图探索(基于物理空间 广播范围)2. 野生宝可梦生成与广播3. 对战系统4. 道具与通信5. 延伸玩法 安全性设计 技术选…...
SAP学习笔记 - 开发26 - 前端Fiori开发 OData V2 和 V4 的差异 (Deepseek整理)
上一章用到了V2 的概念,其实 Fiori当中还有 V4,咱们这一章来总结一下 V2 和 V4。 SAP学习笔记 - 开发25 - 前端Fiori开发 Remote OData Service(使用远端Odata服务),代理中间件(ui5-middleware-simpleproxy)-CSDN博客…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...

