<多线程章节八> 单例模式中的饿汉模式与懒汉模式的讲解,以及懒汉模式中容易引起的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 分数随着序列长度…

构造函数、析构函数、虚函数、成员函数的详细解释

详细解释 构造函数析构函数虚函数成员函数 构造函数 构造函数(Constructor) 是一个特殊的成员函数,当一个对象被创建时,它会自动被调用。构造函数通常用于初始化对象的成员变量。 (就是先设定了一些规矩,到用的时候直…

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

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

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

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

力扣1502. 判断能否形成等差数列(Java,排序法)

Problem: 1502. 判断能否形成等差数列 文章目录 思路解题方法复杂度Code 思路 根据简单的数学知识易得等差数列公差相等。 解题方法 1.对数列排序 2.遍历数列,判断相邻两数的差是否相等。 复杂度 时间复杂度: O ( n ) O(n) O(n) 空间复杂度: O ( 1 ) O(1) O(1) Cod…

设计交换机原理图前应先理清的框图

一、系统布局图 需重点考虑“外壳、电源、风扇、主板(包含MAC、CPU、PHY)、指示灯、管理网口/串口、电口/光口等连接器”在整机中的大致位置,在系统布局图中予以体现。 二、系统框图 (1)电源整体框图; &…

centos做个登录提醒

1.编辑脚本 sudo vim /usr/local/bin/login-notify.sh例如 login-notify.sh #!/bin/bash# 检查是否有一个原始SSH命令,并执行它 if [[ -n "$SSH_ORIGINAL_COMMAND" ]]; thenecho "SSH_ORIGINAL_COMMAND: $SSH_ORIGINAL_COMMAND" >> /va…

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

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

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

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

C#WPF工具提示(ToolTip)实例

本文演示C#WPF工具提示(ToolTip)实例 ToolTip ToolTip是当鼠标移到某个控件上后可以弹出提示的控件 属性说明 1、HasDropShadow 决定工具提示是否具有扩散的黑色阴影,使其和背后的窗口区别开来 2、Placement 使用PlacementMode枚举值决定如何放置工具提示。默认值是M…

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

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,可以保持如…

【学习笔记】CF930E Coins Exhibition

感觉像是之前做过的题的加强版😅 考虑容斥哪些区间不合法。直接处理比较困难,考虑将所有区间按右端点排序,并将端点离散化(将右端点 1 1 1,转化为左闭右开区间),设 d p i , j , k dp_{i,j,k} …

2.1 ppq量化pytorch->onnx

前言 torchvision中加载一个模型,转换为 onnx 格式、导出 quantized graph. code from typing import Iterableimport torch import torchvision from torch.utils.data import DataLoaderfrom ppq import BaseGraph, QuantizationSettingFactory, TargetPlatfor…

代碼隨想錄算法訓練營|第五十五天|1143.最长公共子序列、1035.不相交的线、53. 最大子序和。刷题心得(c++)

讀題 1143.最长公共子序列 自己看到题目的第一想法 看起來跟最長重複子数組很類似,但是要怎麼去推遞推的狀態沒有想法 看完代码随想录之后的想法 看完之後,大概釐清了整體想法,可以想成說,因為我們要考慮的是不連續的子序列&…

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

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

尚硅谷-kubernetes

目录 一、kubernetes概述1、kubernetes基本介绍**2、kubernetes 功能和架构** 一、kubernetes概述 1、kubernetes基本介绍 kubernetes,简称 K8s,是用 8 代替 8 个字符“ubernete”而成的缩写 kubernetes是一个开源的,用于管理云平台中多个…

HTML基本概念:

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