线程安全的单例模式

单列模式是校考中最常考的设计模式之一

啥是设计模式?

设计模式就好比好比向其中的“棋谱”,红方当头炮,黑方马来跳。针对红方的一些走法黑方有一些固定的套路。按照套路来走局势就不会吃亏。

软件开发中有很对常见的“问题场景‘:针对这些场景,大佬总结出了一些固定的套路,按照这个套路来实现代码也不会太吃亏。

单列模式能保证某个程序中只存在唯一一份实例,而不会创建出多个实例。

1.饿汉模式

类加载的同时创建实例

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

这个类被加载的时候,就会初始化这个 静态成员,实例创建的时机非常早,就是用”饿汉“。      后续在别的代码中,尝试new这个Singleton,就会直接编译报错!!  这个写法如果面对反射,当然是无能为力的。

2.懒汉模式

类加载的时候不创建实例,第一次使用的时候才创建实例 

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

 如果是首次调用getInstance,那么此时instance引用为null,就会进入if条件,从而把实例创建出来。如果是后续再次调用getInstance,由于instance已经不再是null此时就不会在进入if,直接返回之前创建好的引用了。      这样设定,仍然可以保证,该类的实例是唯一一个,与从同时,创建实例的时机就不是程序驱动时了,而是第一次调用getInstance的时候这个操作执行的时机就不知道了就看你程序的实际需求,大概率要比饿汉这种要晚一些甚至有可能整个程序压根用不到这个方法,就把创建的操作给剩下了。

在计算机中“懒”这个茨并不是贬义词而是褒义词。懒的思想就非常有意义

比如有一个非常大的文件(10GB)

有一个编辑器,使用编辑器打开这个文件.

如果是按照“饿没”的方式;编辑器就会先把这10-GB的数据都加载到内存中,然后再进行统一-的展示.   即使加载了这么多数据,用户还得一点点看,没法一下子看完这么多

如果是按照"懒汉"的方式,编辑器就会只读取一-小部分数据(比如只读10KB),把这10KB先展示出来.随着用户进行翻页之类的操作,再继续读后续的数据.

3.多线程~~

一个关键问题:上述编写的代码,饿汉模式和懒汉模式,是否线程安全的??

结合之前讨论过的一些线程不安全的一些原因~~

饿汉模式

对于饿汉来说,getInstance直接返回instance实例,整个操作本质上就是“读操作”,多个线程读取同一个变量,是不是线程安全的?? 是线程安全的!!

懒汉模式

如果按上述的图列执行这就导致实例new了两次!!,这就不是单列模式了,就有bug了!!        懒汉模式,线程不安全,在多线程环境下可能会创建多个实例!!

懒汉模式改进

如何改进懒汉模式,让他能够成为线程安全的代码??   加锁,synchronized

  public static Singleton getInstance(){if(instance == null){synchronized(locker){instance = new Singleton();}}return instance;}

这样加锁就安全了吗??

多线程代码,其实是非常复杂的,代码稍微变换一点,结论就截然不同!! 因此可千万不要以为,代码中写了synchronized就一定线程安全,不写synchronized就一定线程不安全!!一定要具体问题具体分析,分析这个代码在各种调度执行顺序下可能发生的情况,确保每个情况都是正确的!!

此处想要让代码执行正确,其实是需要把if和new两个打包成一个原子的!!

更合理的做法,应该是把synchronized套到if外头

此时就可以确保,一定是t1执行完new操作,执行完修改Instance之后,再回到t2执行if,t2的if条件就不会成立了,t2就会直接返回了。

上述代码仍然存在一些问题

如果Instance已经创建过了,此时后续再调用getInstance就都是直接返回实例了吧(此处的操作就是纯粹的读操作了,也就不会有线程安全问题了),此时针对这个已经没有线程安全问题的代码,仍然是每次调用都先加锁再解锁,此时,效率就非常低了 !!

加锁就意味着可能会产生阻塞,一旦线程阻塞,啥时候能解除,就不知道(你可以认为,只有一个代码里加锁了,基本就注定和“ 高性能 ”无缘)。

public static SingletonLazy getInstance(){//如果Instance 为null,就说明是首次调用,首次调用就需要考虑线程安全问题,就要加锁//如果非null,就说明是后续调用,就不必加锁了if(instance == null){synchronized (Locker){if(instance==null){instance = new SingletonLazy();}}}return instance;}

这俩个if看起来是两个一样的条件,但实际上,这俩条件结果可能是相反的!!

这个synchronized就可能让当前这个线程阻塞,阻塞的过程中就可能有别的线程修改了Instance了!!这俩个if中间隔的时间,可能是沧海桑田。

第一个if判定的是是否要加锁,第二个if判定的是是否要创建对象巧了!!这俩if条件相同了。          线程安全&执行效率  “双重校验锁”

代码仍然有问题

指令重排序,引起的线程安全问题。指令重排序,也是编译器优化的一种方式。 

这行代码,其实可以拆成三个大步骤。(不是三个指令)

1.申请一段内存空间

2.在这个内存上调用构造方法,创建出这个实例

3.把这个内存地址赋值给Instance引用变量

正常情况下,上述代码按照1,2,3的顺序来执行的,但是编译器也可能优化成1,3,2的顺序来执行,无论是1,2,3还是1,3,2在单线程下,都是可以的~~

但是,如果是在多线程下,指令重排序,就可能引入问题了!!

t1按照1,3,2的方式来执行这里的new操作

上述代码,有于t1线程执行完1,3之后,在要执行2的时候被调度走,这时候instance指向的是一个非null的,未初始化的对象, 而t2线程判定instance == null不成立,就会直接return。如果t2继续使用instance里面的属性或者方法,就会出现问题(此时这里的属性都是未初始化的“全0”值)就可能会引起代码的逻辑出现问题。

解决方法

解决上述问题,核心思路还是volatile

volatile有两个功能:

1.保证内存可见性,每次访问变量必须都要重新读取内存,而不会优化到寄存器/缓存中。

2.禁止指令重排序,针对这个呗volatile修饰的变量的读写操作相关指令,是不能被重排序的!!

这个时候,针对这个变量的读写操作,就不会出现重排序了,此时执行顺序一定是1,2,3也就杜绝了上述问题。

小结

class SingletonLazy{//这个引用指向唯一实例,这个引用先初始化为null,而不是立即创建实例private volatile static SingletonLazy instance = null;private static Object Locker = new Object();public static SingletonLazy getInstance(){//如果Instance 为null,就说明是首次调用,首次调用就需要考虑线程安全问题,就要加锁//如果非null,就说明是后续调用,就不必加锁了if(instance == null){synchronized (Locker){if(instance==null){instance = new SingletonLazy();}}}return instance;}private SingletonLazy(){}
}

1.在合适的位置进行加锁.

2.该加的时候加,不该加的时候不要加,避免效率受到影响避免双重if.

3.通过volatile禁止这里的重排序,避免出现线程安全问题

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

【云岚到家】-day09-1-项目迁移6-秒杀抢购介绍

【云岚到家】-day09-1-项目迁移6-秒杀抢购介绍 1 购物车1.1 交互流程1.2 Redis数据结构1.3 表设计1.4 定时同步购物车 2 签到活动2.1 需求分析2.2 系统设计2.2.1 表设计2.2.2 接口设计2.2.3 如何计算连续签到天数2.2.4 如何获取匹配签到奖励规则2.2.5 签到代码 3 秒杀抢购介绍1…

探索LINQ在C#中的应用:从基本查询到数据联接

LINQ(语言集成查询)是微软为.NET框架开发的一种强大功能,于2007年作为C# 3.0和Visual Basic .NET 9.0的一部分引入。LINQ的诞生旨在提供一种一致且直观的方式来查询和操作数据,无论数据来源是内存中的集合、数据库还是XML文档。 …

FileLink如何帮助医疗行业实现安全且高效的跨网文件交换

在当今数字化时代,医疗行业在快速发展的同时,也面临着数据安全和信息流转效率的双重挑战。患者的健康记录、影像数据、检查报告等大量敏感信息需要在不同医院、诊所、实验室和保险公司之间高效、迅速地传递。然而,传统的邮件、传真和纸质文件…

windows工具 -- 一个定时播放音乐并延迟锁屏的bat脚本(专注模式) 每隔45分钟电脑自动锁屏

目的 由于静脉曲张比较难受, 不能长时间坐着, 需要有一个强迫休息的功能: 工作45分钟45分钟到了, 自动播放音乐, 并提示30s后进行锁屏 创建bat脚本 创建一个 .txt 文本文件, 然后修改后缀名为 .bat 右键记事本打开, 复制以下代码保存 echo off :: 等待45分钟 TIMEOUT /T 2700…

opencv_相关的问题

Debug模型下运行&#xff0c;在命令行窗口会有一些error相关的log信息。 通过调整log的等级&#xff0c;屏蔽掉INFO的log信息 #include <opencv2/core/utils/logger.hpp>cv::utils::logging::setLogLevel(cv::utils::logging::LOG_LEVEL_ERROR);

深度学习-44-大模型预训练的数据预处理及大模型的分类

文章目录 1 大模型的分类1.1 按任务类型分类1.1.1 生成式模型1.1.2 判别式模型1.1.3 混合模型1.2 按数据模态分类1.2.1 单模态模型1.2.2 多模态模型1.3 按训练方法分类1.3.1 预训练模型1.3.2 从零训练模型1.3.3 迁移学习模型1.4 按应用领域分类1.4.1 自然语言处理模型1.4.2 计算…

zabbix安装基础配置

服务器准备&#xff0c;两台服务器 创建zabbix用户组&#xff0c;新增zabbix用户&#xff0c;并将zabbix用户加入zabbix用户组&#xff0c;设置zabbix用户不允许登录系统权限 [rootlocalhost ~]# groupadd zabbix [rootlocalhost ~]# useradd -g zabbix zabbix [rootlocalhost…

点云的多尺度计算

1.计算点云多尺度特征的方法 1) K近邻,然后通过VoxelGrid进行下采样。 参考文献: Timo Hackel, J. D. W., Konrad Schindler "FAST SEMANTIC SEGMENTATION OF 3D POINT CLOUDS WITH STRONGLY VARYING DENSITY." Schindler, T. H. J. D. W. K. "Contour dete…

如何将各类GIS格式数据转换为带坐标信息的CVS或EXCEL格式

介绍如何利用GIS数据转换器-矢量将各类GIS格式数据&#xff08;SHP\TAB\KML\GEOJSON\GPKG\dxf等&#xff09;转换为带坐标信息的CVS或EXCEL格式。 打开GIS数据转换器-矢量 2.选择要转换的矢量数据 3.选择要输出的格式为CSV或微软开放表格格式 4.选择输出字段 5.选择输出目录 6…

Latex之LNCS模板——Title Suppressed Due to Excessive Length

1、问题描述 使用LNCS模板时&#xff0c;文章右上角显示“Title Suppressed Due to Excessive Length”。原因&#xff1a;文章的标题太长&#xff0c;放不下。 2、解决办法 使用 \titlerunning{} 设置一个短标题。

DPI-MoCo:基于深度先验图像约束的运动补偿重建用于四维锥形束CT (4D CBCT)|文献速递-基于深度学习的病灶分割与数据超分辨率

Title 题目 DPI-MoCo: Deep Prior Image ConstrainedMotion Compensation Reconstruction for 4D CBCT DPI-MoCo&#xff1a;基于深度先验图像约束的运动补偿重建用于四维锥形束CT (4D CBCT) 01 文献速递介绍 安装在直线加速器上的N板锥束计算机断层扫描&#xff08;CBCT&…

6大国有银行软开的薪资待遇清单

牛客上刷到一条关于计算机专业值得去的银行软开清单,其中对 6 大国有银行软开的薪资待遇分析我觉得很有必要同步给大家看一看。 截图信息来自牛客的漫长白日梦 其中邮储软开是最值得推荐的(offer 投票没输过),二线城市转正后第一个完整年的收入在 30 万左右,一线城市更高…

深度学习之Dropout

1 Dropout 系列问题 1.1 为什么要正则化&#xff1f; 深度学习可能存在过拟合问题——高方差&#xff0c;有两个解决方法&#xff0c;一个是正则化&#xff0c;另一个是准备更多的数据&#xff0c;这是非常可靠的方法&#xff0c;但你可能无法时时刻刻准备足够多的训练数据或…

京东AI单旋旋转验证码98准确率通杀方案

注意,本文只提供学习的思路,严禁违反法律以及破坏信息系统等行为,本文只提供思路 如有侵犯,请联系作者下架 本文滑块识别已同步上线至OCR识别网站: http://yxlocr.nat300.top/ocr/other/12 京东单旋验证码最近更新了,使用AI生成,要求识别角度,以下是部分数据集: 接下…

three.js 如何简单的实现场景的雾

three.js 如何简单的实现场景的雾 https://threehub.cn/#/codeMirror?navigationThreeJS&classifybasic&idsceneFog import * as THREE from three import { OrbitControls } from three/examples/jsm/controls/OrbitControls.js import { GLTFLoader } from three…

算法定制LiteAIServer摄像机实时接入分析平台烟火检测算法的主要功能

在现代社会&#xff0c;随着人工智能技术的飞速发展&#xff0c;智能监控系统在公共安全领域的应用日益广泛。其中&#xff0c;烟火检测作为预防火灾的重要手段&#xff0c;其准确性和实时性对于减少火灾损失、保障人民生命财产安全具有重要意义。而算法定制LiteAIServer烟火检…

C++中sizeof运算符的案例分析

分析案例 在网上看到一个关于sizeof疑问的文章&#xff0c;所以就想认真研究下&#xff0c;例子代码如下&#xff1a; #include<iostream> using namespace std; int main(void) {cout << sizeof("123456789") << endl; //10cout << siz…

centos7 部署 Ollama,过程及遇到的问题(上篇)

背景&#xff1a;为了搭建 Dify llama3 实现大模型本地化学习。 材料&#xff1a; 1、centos 7.x 2、网络要通 制作&#xff1a; 1、更新YUM源 1、备份yum源 mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup2、下载阿里yum wget -O /e…

openGauss数据库-头歌实验1-5 修改数据库

一、查看表结构与修改表名 &#xff08;一&#xff09;任务描述 本关任务&#xff1a;修改表名&#xff0c;并能顺利查询到修改后表的结构。 &#xff08;二&#xff09;相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 1.如何查看表的结构&#xff1b; 2.如…

一文学会编写大模型备案安全评估报告「小白也可学会」

文章目录 一、语料安全评估 (一) 评估内容 (二) 评估结论 二、模型安全评估 三、安全措施评估 四、总体结论 适用于不会大模型备案过程中对大模型备案安全评估报告不会如何编写的业务人员。 *图&#xff1a;大模型备案全套素材文件 一、语料安全评估 (一) 评估内容 文本…