<多线程章节八> 单例模式中的饿汉模式与懒汉模式的讲解,以及懒汉模式中容易引起的Bug

💐专栏导读

本篇文章收录于多线程,也欢迎翻阅博主的其他文章,可能也会让你有不一样的收获😄
🌷JavaSE 🍂多线程 🌾数据结构

文章目录

  • 💐专栏导读
  • 💡饿汉模式
  • 💡懒汉模式
  • 💡懒汉模式多线程版
  • 💡volatile防止指令重排序

单例模式是一种经典的设计模式了,它的作用就是保证在有些场景下, 需要一个类只能有一个对象,而不能有多个对象,比如像你以后娶媳妇,你娶媳妇肯定是只能娶一个,而不能娶两个;

但是,问题来了,一个类只需要一个对象,那在new对象的时候只new一次对象不就可以了么,为什么还要弄个这么麻烦的东西呢?

因为啊,只new一次对象确实是只有一个,但是呢,如果你在写代码的过程中忘了呢,然后又new了一次,这种概率是很大的,毕竟,人是最不靠谱的动物😅,就像是有一句话说的好:宁可相信世界上有鬼,也不要相信男人的那张嘴😂,所以的,为了防止这种失误发生,就有了单例模式,在Java中也有许多类似的机制,比如final,就会保证修饰的变量肯定是不能改变的;@override,保证你这方法肯定是一个重写方法;**interface,**保证肯定要重写接口里面的方法;这些都是在语法方面进行了一些限制,但是,在语法方面,对于单例并没有特定的语法,所以,这里就通过编程技巧来达到类似的限制效果;

单例模式的两种实现方式:

💡饿汉模式

1.在类中实例化类的对象,给外界提供一个方法来使用这个对象;

2.将构造方法用private修饰,保证在类外不能再实例化这个类对象

public class SingleTon {//在类的内部实例化对象public static SingleTon instance = new SingleTon();//定义一个方法,用来获取这个对象//后序如果类外的代码想要使用对象时,直接调用这个方法即可public static SingleTon getInstance() {return instance;}//设置一个私有的构造方法,保证在这个类外无法实例化这个对象private SingleTon(){}
}

在这里插入图片描述

可以看到,这里的对象被static修饰,所以在类被加载的时候创建,创建的时机就比较早,并且被static修饰的对象只会被创建一次,所以这种在类加载时就创建实例的模式称为饿汉模式

💡懒汉模式

懒汉模式单线程版:

这样的写法与上面的相同点就是:同样在类外不能再第二次实例化对象,不同点是:将创建对象的时机放在getInstance方法中,这样在类加载的时候就不会创造实例,而是当第一次调用这个方法时才会去创建

public class SingleTon {public static SingleTon instance = null;//定义一个方法,用来获取这个对象//后序如果类外的代码想要使用对象时,直接调用这个方法即可public static SingleTon getInstance() {//懒汉模式if(instance == null) {instance = new SingleTon();}return instance;}//设置一个私有的构造方法,保证在这个类外无法实例化这个对象private SingleTon(){}
}

在这里插入图片描述

💡懒汉模式多线程版

在线程安全方面,上面的饿汉模式是线程安全的,而懒汉模式在多线程下是不安全的;

因为,如果多个线程同时访问一个变量,那么不会出现不安全问题,如果多个线程同时修改一个变量,就有可能出现不安全问题;

饿汉模式下,只进行了访问,没有涉及到修改

在这里插入图片描述

懒汉模式下,不仅进行了访问,还涉及了修改,那么下面就讲解以下懒汉模式在多线程下如何会产生不安全

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

既然出现了不安全问题,那么如何将懒汉模式修改成安全的呢?

💡方法:进行加锁,使线程安全

在这里插入图片描述

但是,如果锁加在这个地方,仍然是不安全的,因为,这样还是会进行穿插执行,并没有保证它是一个整体(f非原子性)

在这里插入图片描述

并不是加了锁就安全,只有锁加对了才会安全,在加锁的时候要保证以下几方面:

1.锁的 {} 的范围是合理的,能够把需要作为整体的每个部分都包括进去;

2.锁的对象能够起到锁竞争的效果;

懒汉模式多线程版改进👇

将if语句和new都放在锁里面称为一个整体,这样就避免了会穿插执行;

    public static SingleTon getInstance() {synchronized (SingleTon.class) {if(instance == null) {instance = new SingleTon();}}return instance;}

但是上述代码还有一个问题,每当调用getInstance时,都会尝试去进行加锁,而加锁是一个开销很大的操作,而这里的懒汉模式之所以会出现线程不安全问题,是因为只是在在第一次调用getInstance方法进行new对象时,可能会出现问题,但是,只要new完对象以后,就不用再进行锁竞争再去进行if判断了,直接访问就可以了,所以再次进行优化👇:

    public static SingleTon getInstance() {//在最外面在进行一次判断if(instance == null) {synchronized (SingleTon.class) {if(instance == null) {instance = new SingleTon();}}}return instance;}

在第一次实例化对象后,以后再调用个getInstance方法时,就不会再创建对象,而且也不会再去获取锁,因为,第一个if判断语句都不会进去,所以不会执行到加锁的语句;

上面的单例模式看着好像是完全没问题了,但是,还是有一个问题,就是可能会触发指令重排序问题,所以就需要使用volatile解决指令重排序问题

💡volatile防止指令重排序

指令重排序:编译器会保证在你代码逻辑不变的情况下,对代码进行优化,使代码的性能得到提高,这样的操作称为指令重排序;

举个例子:

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

在代码中,在实例化对象这一步可能会出现指令重排序问题,下面就来讲解一下为什么👇

在这里插入图片描述

在这里插入图片描述

对于上述的指令重排序问题,解决方案就是:使用volatile关键字修饰singleTon

**线程安全的单例模式(懒汉模式)**👇

public class SingleTon {//使用volatile关键字修饰,防止指令重排序public static volatile SingleTon singleTon = null;public static SingleTon getSingleTon() {if(singleTon == null) {synchronized (SingleTon.class) {if(singleTon == null) {singleTon = new SingleTon();}}}return singleTon;}private SingleTon() {};}

💡💡这里再次提醒,使用单例模式要注意三个要点:

  • 加锁
  • 两层if判断
  • 使用volatile修饰引用,防止指令重排序

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

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

相关文章

从 Seq2Seq 到 Attention:彻底改变序列建模

探究Attention机制和意力的起源。 简介 在这篇博文[1]中,将讨论注意力机制的起源,然后介绍第一篇将注意力用于神经机器翻译的论文。由于上下文压缩、短期记忆限制和偏差,具有 2 个 RNN 的 Seq2Seq 模型失败了。该模型的 BLEU 分数随着序列长度…

【VPX630】青翼 基于KU115 FPGA+C6678 DSP的6U VPX通用超宽带实时信号处理平台

板卡概述 VPX630是一款基于6U VPX总线架构的高速信号处理平台,该平台采用一片Xilinx的Kintex UltraScale系列FPGA(XCKU115)作为主处理器,完成复杂的数据采集、回放以及实时信号处理算法。采用一片带有ARM内核的高性能嵌入式处理器…

【深度学习】pytorch——快速入门

笔记为自我总结整理的学习笔记,若有错误欢迎指出哟~ pytorch快速入门 简介张量(Tensor)操作创建张量向量拷贝张量维度张量加法函数名后面带下划线 _ 的函数索引和切片Tensor和Numpy的数组之间的转换张量(tensor)与标量…

三十九、CANdelaStudio实践-19服务(ReadDTCInformation)

本专栏将由浅入深的展开诊断实际开发与测试的数据库编辑,包含大量实际开发过程中的步骤、使用技巧与少量对Autosar标准的解读。希望能对大家有所帮助,与大家共同成长,早日成为一名车载诊断、通信全栈工程师。 本文介绍CANdelaStudio的19服务(ReadDTCInformation)编辑,欢迎…

基于STM32C8T6的智能蓝牙小车控制设计

**单片机设计介绍,1655基于STM32C8T6的智能蓝牙小车控制设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计五、 程序文档 六、 结论七、 文章目录 一 概要 基于STM32C8T6的智能蓝牙小车控制设计是基于STM32微控制器和蓝牙模块开发的一种小型智能车辆控制系统…

力扣刷题-队列-滑动窗口最大值

239. 滑动窗口最大值 给定一个数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。 进阶: 在线性时间复杂度内解决此题? …

算法leetcode|86. 分隔链表(rust重拳出击)

文章目录 86. 分隔链表:样例 1:样例 2:提示: 分析:题解:rust:go:c:python:java: 86. 分隔链表: 给你一个链表的头节点 head 和一个特…

ubuntu 22.04 源码安装 apollo 8.0

对于其他的关于GPU的安装包需求,这里不再列出,因为我之前安装过,偷个懒就不写了,哈哈哈哈1, 安装docker 安装docker命令(这里的安装命令都是在docker官网,还有安装包): 1, 设置docker的apt仓库 # Add Do…

IDEA 如何运行 SpringBoot 项目

步骤一:配置 Maven 第一步:用 IDEA 打开项目,准备配置 maven 环境 ,当然如果本地没有提前配置好 maven,就用 IDEA 默认的配置即可 配置 maven 步骤 情况 1:如果本地没有配置过 maven,可以保持如…

Jetpack:025-Jetpack中的多点触控事件

文章目录 1. 概念介绍2. 使用方法2.1 缩放事件2.2 旋转事件2.3 平移事件2.4 综合事件 3. 示例代码4. 内容总结 我们在上一章回中介绍了Jetpack中滚动事件相关的内容,本章回中主要介绍 多点解控事件。闲话休提,让我们一起Talk Android Jetpack吧&#xf…

HTML基本概念:

HTML简介: 超文本标记语言(英语:HyperText Markup Language,简称:HTML)是一种用于创建网页的标准标记语言。 1)、HTML 是用来描述网页的一种语言。 2)、HTML 不是一种编程语言&am…

笔记本电脑搜索不到wifi6 无线路由器信号

路由器更换成wifi6 无线路由器后,手机能搜索到这个无线信号,但是笔记本搜索不到这个无线信号,后网上搜索后发现是无线网卡驱动问题,很多无线网卡使用的是Intel芯片,Intel就此发布了公告,升级驱动就可以彻底…

我和云栖有个约会

开端,似曾相识的云栖 2023年阿里云云栖大会在云栖小镇举办,云栖小镇?在2020年的时候,曾经来过这里参加竞赛。时隔三年,身份变换,以开发者的身份,收到阿里云开发者社区的邀请,正好有…

linux 报错

输入 pip install -U openmim报错 有可能是服务器在其他国家,需要手动设置 把这三行复制到~/.bashrc里 export http_proxyhttp://127.0.0.1:3128 export https_proxy${http_proxy} export ftp_proxy${http_proxy}source ~/.bashrc

第57篇-某钩招聘网站加密参数分析【2023-10-31】

声明:该专栏涉及的所有案例均为学习使用,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关!如有侵权,请私信联系本人删帖! 文章目录 一、前言二、网站分析1.X-S-HEADER参数2.请求参数data3.响应机密值data一、前言 网址: aHR0cHM6Ly93d3cubGFnb3UuY29t…

[NSSRound#6 Team]check(Revenge)

文章目录 考点tarfile文件覆盖漏洞(CVE-2007-4559)PIN码计算 解题过程非预期解预期解 考点 tarfile文件覆盖漏洞(CVE-2007-4559) Python 中 tarfile 模块中的extract、extractFile和extractall 函数中的目录遍历漏洞 允许 用户协…

`.NET Web`新人入门必学项目`EarthChat`

.NET Web新人入门必学项目EarthChat EarthChat是一个基于.NET 7的实战项目,EarthChat提供了很多的最佳实践,EarthChat的目标也是成为一个很多人都喜欢的大型聊天业务系统,并且将结合SKAI大模型进行打造智能业务系统,在EarthChat中…

Pytorch 文本情感分类案例

一共六个脚本,分别是: ①generateDictionary.py用于生成词典 ②datasets.py定义了数据集加载的方法 ③models.py定义了网络模型 ④configs.py配置一些参数 ⑤run_train.py训练模型 ⑥run_test.py测试模型 数据集https://download.csdn.net/download/Victor_Li_/88486959?spm1…

narak靶机攻略

narak靶机攻略 扫描 渗透 cewl http://10.4.7.158 > use1.txthydra -L use1.txt -P use1.txt http-get://10.4.7.158/webdav -V -t 50 -fyamdoot:Swargcadaver http://10.4.7.158/webdav<?php $ip10.4.7.158; $port12138; $sock fsockopen($ip, $port); $descriptors…

识别flink的反压源头

背景 flink中最常见的问题就是反压&#xff0c;这种情况下我们要正确的识别导致反压的真正的源头&#xff0c;本文就简单看下如何正确识别反压的源头 反压的源头 首先我们必须意识到现实中轻微的反压是没有必要去优化的&#xff0c;因为这种情况下是由于偶尔的流量峰值,Task…