javaEE初阶——多线程(四)

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享多线程专题的第四篇(关于多线程代码案例中的单例模式)
如果有不足的或者错误的请您指出!

目录

  • 九、多线程代码案例(单例模式)
    • 1.单例模式
      • 1.1饿汉模式
      • 1.2懒汉模式
      • 1.3使用场景
      • 1.4上述单例模式的线程安全问题
      • 1.5指令重排序问题

九、多线程代码案例(单例模式)

1.单例模式

单例模式是设计模式里面比较简单的一种,顾名思义,单例就是只有一个实例,也就是对于进程中的某个类,它只能实例化一个对象,不会new出来多的对象
那我们如何保证一个类只能有一个对象呢???单凭我们口头保证肯定是不行的,我们就需要通过特定代码的手段来实现这个功能
单例模式我们主要认识两种写法:饿汉模式、懒汉模式

1.1饿汉模式

表示这个类的唯一实例创建得比较早,类似于饿了很久的人,一看到吃的就迫不及待想去吃
用代码体现就是:

public class Singleton {private static Singleton instance = new Singleton();public static Singleton getInstance(){return instance;}
}

static修饰的其实是类属性,就是在"类对象"上的,每个类的类对象在jvm中只有一个,那么里面的静态成员就只有一个了
此处后续需要使用这个类的实例,就可以直接通过getInstance来获取已经new好的这个,而不是重新new
但是我们怎么保证,这个类就只能实例化一次呢??我们直接将构造方法私有化即可

public class Singleton {private static Singleton instance = new Singleton();public static Singleton getInstance(){return instance;}private Singleton(){}//代码里面不需要有任何逻辑
}

将构造方法私有化是单例模式里面最核心的一步,类之外的代码尝试实例化对象的时候,就必须要通过构造方法,但是由于构造方法是私有的,无法调用,尝试实例化的时候就会编译出错
我们验证一下:
在这里插入图片描述
当我们尝试实例化对象:
在这里插入图片描述
就会编译出错

可能有人会问两种极端情况
(1)如果我在一个Singleton类里面实例化多个对象,那不就打破了单例模式了嘛??
实际上,如果你是这个类的作者,你一定不会去这么做;如果你是别人,使用这个类的时候,如果要进行修改,一般也要经过你的审核
(2)那么我们可以通过反射机制拿到私有的构造方法嘛??
原则上是可以的,但是在实际开发中,反射机制并不常见,甚至不能乱用,特殊场景下回需要用到反射,但是使用反射要付出很大的代价(会严重影响代码的可读性和封装性)

1.2懒汉模式

在计算机领域,"懒"往往是提高效率的表现
在懒汉模式中,不是在程序启动的时候就实例化好唯一对象了,而是在后续使用这个类的时候才去创建实例,此时如果不去使用这个类,那么创建实例的代价就很好地省下了

public class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance(){if(instance == null){instance = new SingletonLazy();}return instance;}private SingletonLazy(){}}

什么时候使用这个类就什么时候创建这个实例,本质上就是能偷懒的,能少做的就少做

1.3使用场景

代码中的有些对象,本身就不适合是有多个实例的,从业务角度就应该是单个实例

比如,你写的服务器,要从硬盘上加载100G的数据到内存里面,肯定要写一个类,封装上述加载操作,并且写一些获取/处理业务数据的业务逻辑

这样的类就应该是单例的,一个实例就管理100G的数据,搞N个实例就是N*100G的内存数据,机器肯定吃不消,也没必要,因为都是重复的数据

例如在java的JDBC中,DataSourse就应该是单例的,这种对象就类似于配置管理的对象(存储服务器的地址,端口号,用户名,密码,选项参数…)

1.4上述单例模式的线程安全问题

我们需要考虑,如果有多个线程同时调用getInstance(),线程是否安全??
如果是在饿汉模式下,实例创建的时间是程序启动的时候,比main线程调用还早,因而后续使用这个类的时候,调用getInstance()一定比创建实例要晚,此时就只是读取上述变量的值了,而在多个线程里同时读取同一个变量,是不会有线程安全问题的
我们主要是来看懒汉模式下的线程安全问题
在这里插入图片描述
此时一旦出现这种执行顺序,那么就会创建多一个实例
因此我们需要做的就是将if操作和new操作打包成一个原子操作
那么就要进行加锁操作

public class SingletonLazy {private static SingletonLazy instance = null;private static Object locker = new Object();public static SingletonLazy getInstance(){synchronized (locker) {if(instance == null){instance = new SingletonLazy();}}return instance;}private SingletonLazy(){}}

但是还有一个细节问题,我们知道在懒汉模式下,只有在第一次调用getInstance()才是创建实例的.而一旦创建好了,后面的就只是读操作了
但是如果我们单纯这样加,那么后面尽管是读,也是需要加锁的,而加锁的开销是很大的,也有可能导致线程堵塞
因此我们可以在外层在加上if条件判断,如果需要创建实例,才加锁

public 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(){}}

此时两个if的作用是不一样的,外层的if是为了防止没必要的加锁操作,里层的if是判断要不要加锁(如果两个线程都进去了第一层if,那么第二层if就是防止多创建对象)

同时,为了防止编译器进行优化操作,我们还需要对instance加上volatile关键字

public class SingletonLazy {private static volatile 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(){}}

1.5指令重排序问题

指令重排序实际上也是编译器优化的一种策略
我们写的代码在被编译成一条一条的二进制指令后,正常来说CPU都是按照顺序一条一条执行,但是编译器比较智能,会根据实际情况,调整指令执行的顺序,调整的目的就是为了提高效率
在单线程下,编译器的优化策略是比较安全的,能够保证优化前后代码的逻辑不变,但是在多线程环境下,就可能出现问题
就拿我们上面的单例模式 - 懒汉模式来说

instance = new SingletonLazy();

这一句代码,我们简单来看就可以分成3个步骤
(1)申请内存空间
(2)调用构造方法
(3)把此时内存的地址赋值到instance
在指令重排序下,可能会出现1,2,3或者1,3,2两种执行顺序(但是1一定是再前面的),在单例模式下这两种都是OK的
但是在多线程就可能会出现下面的情况:
在这里插入图片描述
在t1线程进行完地址赋值操作后,意味着instance就是非null,只是此时执行的对象是一个未初始化的对象
此时t2线程恰好进来,由于instance已经非空,第一个if的条件无法满足,就直接return了,如果在这个时候,进行Singleton s = …,s.func(),这里的后续操作都是针对未初始化的对象进行操作的,会出现严重的问题

解决上述问题的方法还是使用volatile
即volatile不仅能够解决内存可见性的线程安全问题,还能解决指令重排序的问题

感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/307330.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【刷题】图论——最小生成树:Prim、Kruskal【模板】

假设有n个点m条边。 Prim适用于邻接矩阵存的稠密图,时间复杂度是 O ( n 2 ) O(n^2) O(n2),可用堆优化成 O ( n l o g n ) O(nlogn) O(nlogn)。 Kruskal适用于稀疏图,n个点m条边,时间复杂度是 m l o g ( m ) mlog(m) mlog(m)。 Pr…

华媒舍:7种方式,打造出旅游媒体套餐

现如今,伴随着旅游业发展与繁荣,更多旅游业发展从业人员越来越重视产品营销品牌基本建设,希望可以将自己的度假旅游产品和服务营销推广给更多的潜在用户。而建立一个优秀的旅游业发展媒体套餐内容品牌是吸引目标客户的重要步骤。下面我们就详…

streamlit 大模型前段界面

结合 langchain 一起使用的工具,可以显示 web 界面 pip install streamlit duckduckgo-search 运行命令 streamlit run D:\Python_project\NLP\大模型学习\test.py import os from dotenv import load_dotenv from langchain_community.llms import Tongyi load…

Flutter仿Boss-6.底部tab切换

效果 实现 图片资源采用boss包中的动画webp资源。Flutter采用Image加载webp动画。 遇到的问题 问题:Flutter加载webp再次加载无法再次播放动画问题 看如下代码: Image.asset(assets/images/xxx.webp,width: 40.w,height: 30.w, )运行的效果&#xf…

图片过大怎么改小?图片尺寸在线修改的方法

在通过电子邮件或即时消息传递应用程序发送图片时,某些平台上传图片的时候经常遇到尺寸不符合的情况,通过在线修改图片尺寸,您可以调整图片的大小,以满足限制要求,并确保图片可以顺利地通过电子邮件或消息传递应用程序…

HarmonyOS4 页面路由

Index.ets: import router from ohos.routerclass RouterInfo {// 页面路径url: string// 页面标题title: stringconstructor(url: string, title: string) {this.url urlthis.title title} }Entry // 入口組件 Component struct Index {State message: string 页面列表// …

Selenium+Chrome Driver 爬取搜狐页面信息

进行selenium包和chromedriver驱动的安装 安装selenium包 在命令行或者anaconda prompt 中输入 pip install Selenium 安装 chromedriver 先查看chrome浏览器的版本 这里是 123.0.6312.106 版 然后在http://npm.taobao.org/mirrors/chromedriver/或者https://googlechrom…

【深度学习实战(2)】如何使用matplotlib.pyplot模块记录自己的训练,验证损失

一、matplotlib库 在我们自己训练模型时,常常会使用matplotlib库来绘制oss和accuracy的曲线图,帮助我们分析模型的训练表现。 matplotlib库安装:pip install matplotlib 二、代码 import matplotlib.pyplot as plt import torch import to…

2024年蓝桥杯40天打卡总结

2024蓝桥杯40天打卡总结 真题题解其它预估考点重点复习考点时间复杂度前缀和二分的两个模板字符串相关 String和StringBuilderArrayList HashSet HashMap相关蓝桥杯Java常用算法大数类BigInteger的存储与运算日期相关考点及函数质数最小公倍数和最大公约数排序库的使用栈Math类…

Redis系列之基于Linux单机安装

Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。最近学习需要用到Redis,所以就去Linux服务器上部署一个,做下记录,方便…

【网站项目】数学辅导微信小程序

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

Ollama、FastGPT大模型RAG结合使用案例

参考: https://ollama.com/download/linux https://doc.fastai.site/docs/intro/ https://blog.csdn.net/m0_71142057/article/details/136738997 https://doc.fastgpt.run/docs/development/custom-models/m3e/ Ollama作为后端大模型加载运行 FastGPT作为前端页面聊天集成RA…

『FPGA通信接口』汇总目录

Welcome 大家好,欢迎来到瑾芳玉洁的博客! 😑励志开源分享诗和代码,三餐却无汤,顿顿都被噎。 😭有幸结识那个值得被认真、被珍惜、被捧在手掌心的女孩,不出意外被敷衍、被唾弃、被埋在了垃圾堆。…

【IR-SDE】Image Restoration SDE项目演示运行app.py

背景: code:GitHub - Algolzw/image-restoration-sde: Image Restoration with Mean-Reverting Stochastic Differential Equations, ICML 2023. Winning solution of the NTIRE 2023 Image Shadow Removal Challenge. paper: Official PyTorch Implementations o…

ppt技巧:如何将Word文档大纲中导入到幻灯片中?

在PowerPoint中,将Word文档的大纲导入到新的幻灯片是一种非常实用的技巧。以下是详细的步骤: 首先,需要打开PowerPoint软件并打开原始的幻灯片文件。 在PowerPoint的顶部【开始】菜单栏中,找到并点击“新建幻灯片”按钮&#xff0…

最新常见的图数据库对比,选型,架构,性能对比

图数据库排名 地址:https://db-engines.com/en/ranking/graphdbms 知识图谱查询语言 SPARQL、Cypher、Gremlin、PGQL 和 G-CORE 语法 / 语义 / 特性SPARQLCypherGremlinPGQLG-CORE图模式匹配查询语法CGPCGPCGP(无可选)1CGPCGP语义子图同态、包 2无重复边、包 2子…

Django Rest Framework的序列化和反序列化

DRF的序列化和反序列化 目录 DRF的序列化和反序列化Django传统序列化Django传统反序列化安装DRF序列化器serializers序列化反序列化反序列化保存instance和data CBV和APIView执行流程源码解析CBV源码分析APIView源码分析 DRF的Request解析魔法方法__getattr__ 什么是序列化&…

动力学-坐标系

文章目录 1 转动坐标系2 运动坐标系3 刚体运动参数• 拉格朗日建立机器人动力学方程需用齐次变换矩阵,计算效率低。优点是可以写成状态方程的形式,便于运用控制方法。 • 牛顿—欧拉动力学方程可得到一组正向和反向递推方程,显著优点是可把驱动力矩的计算时间缩短到可实时控…

物联网实战--驱动篇之(五)TEA和AES加密算法

目录 一、前言 二、TEA算法 三、AES算法 四、加解密测试 五、安全性保障 一、前言 物联网的安全性是经常被提及的一个点,如果你的设备之间通讯没有加密的话,那么攻击者很容易就能获取并解析出报文的协议,从而根据攻击者的需要进行设备操…

Redis--16--Spring Data Redis

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 Spring Data Redishttps://spring.io/projects/spring-data-redis 1.依赖2.RedisTemplate3.案例 序列化1.默认是 JdkSerializationRedisSerializer2.添加Redis配置文…