【多线程案例】设计模式-单例模式

news/2024/5/16 0:52:49/文章来源:https://blog.csdn.net/m0_73381672/article/details/133638041

1.单例模式

什么是单例模式?

所谓单例,即单个实例。通过编码技巧约定某个类只能有唯一一个实例对象,并且提前在类里面创建好一个实例对象,把构造方法私有化,再对外提供获取这个实例对象的方法,(方法名通常是用getInstance这个名称)。 

根据创建时机不同,分为两种:

1.类加载的时候创建,也称为饿汉模式。

public class Singleton {//私有构造方法 禁止外界创建实例对象private Singleton() {};//唯一实例对象private static Singleton instance = new Singleton();//为外界提供获取唯一实例的方法public static Singleton getInstance() {return instance;}}

2.在第一次使用的时候创建,也称为懒汉模式  但这种有线程安全问题。

public class SingletonLazy {//私有构造方法private SingletonLazy() {}//实例对象private static SingletonLazy instance = null;//首次调用该方法时才真正创建出实例public static SingletonLazy getInstance() {if(instance == null) {instance = new SingletonLazy();}return instance;}}

总结:

高效性:饿汉模式是在类加载的时候就会创建实例,不管后面用不用得到,都会创建出来。而懒汉模式是只有你真正用了,才会创建出实例,如果不用则不创建,这样也就比较灵活,也就省下了创建实例这一开销。

比如有个非常大的文档(10G)需要打开,有两种方式打开:

  • 先把所有的内容都加载到内存中,然后再显示内容。即饿汉。
  • 先只加载一部分数据到内存,立即显示内容。随着用户翻页,再动态加载其他内容。即懒汉。

为什么懒汉模式会有线程安全问题?

先来说说线程安全问题产生的原因。

  • 如果多个线程同时修改同一个变量,就有可能出现线程安全问题。
  • 如果多个线程同时读取同一个变量,是不会出现线程安全问题的。

饿汉模式中是直接创建实例并返回实例,而懒汉模式是通过判断进行了修改,既读又修改。这种判断再修改就可能会导致线程不安全问题(因为可能会new多次,创建多个实例的话就不是单例模式了)。假设有两个线程 t1,t2,假如t1进行判断instance为null,准备new时,这时候可能会出现t1还没new呢,t2就开始判断instance是否为null。那此时instance肯定为null。这样的话,实例就会被创建多次。显然这就违背了单例模式的要求:单个实例。

如何使懒汉模式线程安全?

进行加锁。  

加锁也得注意咋加,要看加的合不合适,不是说加了就好了。

比如这种加锁:

public class SingletonLazy {//私有构造方法private SingletonLazy() {}//实例对象private static SingletonLazy instance = null;//首次调用该方法时才真正创建出实例public static SingletonLazy getInstance() {if(instance == null) {synchronized (SingletonLazy.class) {instance = new SingletonLazy();}}return instance;}}

缺点:没有使 if判断 和 new操作 成为一个整体,虽然在实例对象的时候加锁了,但是线程在if判断的时候,没有加锁,还是会出现误判。假设有两个线程t1,t2,由于if判断并没有加锁,两个线程是可以同时判断的,如果t2线程刚好在t1线程判断instance为nullt1线程进入new之前或还没new完时t2进行if判断,也是会创建多个实例对象的, 这就导致虽然new的时候加了锁线程是顺序执行的,但new外面的逻辑线程还是随机调度的。于是给整个if上锁。

public class SingletonLazy {//私有构造方法private SingletonLazy() {}//实例对象private static SingletonLazy instance = null;//首次调用该方法时才真正创建出实例public static SingletonLazy getInstance() {synchronized (SingletonLazy.class) {//if和new成为一个整体if(instance == null) {instance = new SingletonLazy();}}return instance;}}

这样加锁弥补了上一个代码的缺点,但是还有一个问题,加锁这种操作就是把调度的随机性改为顺序执行了,那效率,性能必然会大打折扣,况且我们把加锁放在最外面的话,只要用到实例都要加锁,而创建实例对象只有在首次时会发生线程不安全,其实加锁一次就行,用不着回回都进行加锁。这个代码线程虽然是安全了,但是同时效率也降低了,那么有没有一种既能使线程安全又能使效率比较快的代码逻辑呢?当然有,一种方法是在加锁外面再加一层if判断。即两层if。代码如下:

public class SingletonLazy {//私有构造方法private SingletonLazy() {}//实例对象private static SingletonLazy instance = null;//首次调用该方法时才真正创建出实例public static SingletonLazy getInstance() {if(instance == null) {synchronized (SingletonLazy.class) {if(instance == null) {instance = new SingletonLazy();}}}return instance;}}

外面这层if就是用来判断对象是否创建好,如果创建好了,就不用进入外层if加锁,直接执行return。代码效率一下提高。如果没创建好,才会进入并加锁。而不是想上面代码频繁加锁。里面的if是判断是否需要new对象。

但是上面代码还是会有一个问题,就是指令重排序问题。

指令重排序问题是什么?

说到底和内存可见性一样,都是编译器为了增加效率,而对原有代码的执行顺序做出调整。调整的前提是保持逻辑不变。

举个例子:假如我们去超市买东西,需要买菜,买衣服,买首饰,买玩具。此时若按照衣服,玩具,首饰,菜这种顺序效率是最高的。

在单例模式中,new操作,时可能会触发指令重排序问题的,new操作可分为三步:

  1. 申请内存空间。
  2. 在内空间上构造对象(构造方法)。
  3. 给对象引用。

其中1的顺序不变,2和3的顺序是可以换的。执行1->2->3顺序使我们希望的,但若是执行1->3->2顺序,执行到3时,对象虽然不是null了,但是此时的对象还没有初始化,贸然使用是非法的。若有两个线程,一个线程才执行到1->3,另一个线程去使用还未new完的这个对象,就会引发异常。

但虽然这样说,我们不是加了锁吗,那不应该是其他没加锁的线程阻塞等待加锁的这个线程执行完new的三步后,释放锁后,其他线程才能继续执行吗,为什么我加锁的线程还没new完,甚至是还没释放锁呢,其他线程就已经去使用对象了?原因是:另一个线程压根就没进入外层if。一个线程加锁,没拿到锁的等待,不是锁一个线程拿到锁了,不管其他线程在干嘛都得停下来等待,而是执行到有synchronized语句时,才等待。既然这个线程都没进入外层if,肯定碰不到synchronized语句,就会直接return,实例就被拿去使用了。

上述问题的核心是解决指令重排序问题,解决办法就是给实例对象加volatile修饰。

public class SingletonLazy {//私有构造方法private SingletonLazy() {}//实例对象private static volatile SingletonLazy instance = null;//首次调用该方法时才真正创建出实例public static SingletonLazy getInstance() {if(instance == null) {synchronized (SingletonLazy.class) {if(instance == null) {instance = new SingletonLazy();}}}return instance;}}

    

应用场景举例: 

  1. 外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 。
  2.  Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 
  3.  windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 
  4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 
  5.  应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 
  6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 
  7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 
  8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 
  9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
  10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例。

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

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

相关文章

腾讯云/阿里云国际站免费账号:腾讯云国际站如何对象存储cos设置防盗链

简介 为了避免恶意程序使用资源 URL 盗刷公网流量或使用恶意手法盗用资源,腾讯云国际站给用户带来不必要的损失。腾讯云对象存储支持防盗链配置,建议您通过控制台的防盗链设置配置黑/白名单,来进行安全防护。 注意: 如果您访问对…

北京筑龙全面赋能!打造公共资源交易一体化整合“内蒙模式”

近日, 由内蒙古自治区公共资源交易中心(以下简称中心)与北京筑龙联合建设的内蒙古公共资源交易平台一体化整合项目顺利完成验收工作。该平台的顺利验收标志着这个以科技创新和管理创新“双创”为驱动,以实现公共资源交易全流程电子…

Headless CMS(strapi)

Headless CMS(strapi) 玩了玩微信小程序的cms,感觉还挺好的,不过目前处于公测阶段,后续应该还是要收费的,不过这个操作还挺好的。文档地址 不过其获取图片的时候默认用到的是小城云开发环境的链接样式,如果用在公开网…

问题:remote: HTTP Basic: Access denied

参看文章:https://baijiahao.baidu.com/s?id1740126019873950482&wfrspider&forpc 解决方法一 (最有效) 输入:git config --system --unset credential.helper 再次进行 Git 操作,输入正确的用户名,密码即可。

Unity实现设计模式——策略模式

Unity实现设计模式——策略模式 策略模式是一种定义一些列算法的方法,这些所有的算法都是完成相同的工作,只是实现不同。它可以通过相同的方式调用所有的算法,减少各种算法类与使用算法类之间的耦合。 策略模式的 Strategy 类层次为 Contex…

用正则表达式验证用户名和跨域postmessage

正则验证用户名 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> </hea…

AT9110H-单通道低压 H桥电机驱动芯片

AT9110H能够驱动一个直流有刷电机或其它诸如螺线管的器件。输出驱动模块由PMOSNMOS功率管构成的H桥组成&#xff0c;以驱动电机绕组。AT9110H能够提供高达12V1A的驱动输出。 AT9110H是SOP8封装&#xff0c;且是无铅产品&#xff0c;符合环保标准。 AT9110H具有一个PWM (IN1/IN2…

Javascript笔记 rest VS spread

1 rest 2 spread 3 二者区别 在 JavaScript 中&#xff0c;spread 操作符 ... 和 rest 参数都使用三个点 ... 作为前缀&#xff0c;但它们在使用上有一些区别&#xff0c;主要体现在它们的作用和使用场景上。 Spread 操作符 ... 作用&#xff1a; "展开"数组或对象的…

虚拟环境搭建、后台项目创建及目录调整、封装logger、封装全局异常、封装Response、后台数据库创建

1 虚拟环境搭建 #1 虚拟环境作用多个项目&#xff0c;自己有自己的环境&#xff0c;装的模块属于自己的# 2 使用pycharm创建-一般放在项目路径下&#xff1a;venv文件夹-lib文件夹---》site-package--》虚拟环境装的模块&#xff0c;都会放在这里-scripts--》python&#xff0…

Android系统启动之init进程启动+Zygote进程启动分析

一、基础概念理解 init进程 Android系统所有进程的祖先&#xff0c;是Android系统内核初始化完毕后&#xff0c;进入用户空间启动的第一个进程。 Android虚拟机 Dalvik虚拟机是谷歌自己设计的用于Android平台的虚拟机。Android4.4同时提供了Dalvik和ART虚拟机。Android5.0以后…

【C++设计模式之责任链模式:行为型】分析及示例

简介 责任链模式是一种行为型设计模式&#xff0c;它允许将请求沿着处理链传递&#xff0c;直到有一个处理器能够处理该请求。这种模式将请求的发送者和接收者解耦&#xff0c;同时提供了更高的灵活性和可扩展性。 描述 责任链模式由多个处理器组成一个处理链&#xff0c;每…

增强现实抬头显示AR-HUD

增强现实抬头显示&#xff08;AR-HUD&#xff09;可以将当前车身状态、障碍物提醒等信息3D投影在前挡风玻璃上&#xff0c;并通过自研的AR-Creator算法&#xff0c;融合实际道路场景进行导航&#xff0c;使驾驶员无需低头即可了解车辆实时行驶状况。结合DMS系统&#xff0c;可以…

完美收官丨深圳信驰达科技IOTE 2023第二十届国际物联网展参展回顾

►►►展会风采 2023年9月22日&#xff0c;为期三天的IOTE 2023第二十届国际物联网展 • 深圳站在深圳国际会展中心&#xff08;宝安馆&#xff09;9、10、11号馆圆满落幕。本届展会以“IoT构建数字经济底座”为主题&#xff0c;吸引覆盖IoT全栈生态的参展商&#xff0c;展出超…

buuctf PWN warmup_csaw_2016

下载附件&#xff0c;IDA查看 发现直接有显示flag函数 int sub_40060D() {return system("cat flag.txt"); }查看程序起始地址0x40060D ; Attributes: bp-based framesub_40060D proc near ; __unwind { push rbp mov rbp, rsp mov edi, offset comman…

sylar高性能服务器-日志(P1-P6)代码解析+调试分析

文章目录 一、整体结构二、LogEvent三、LogLevel四、LogFormatter五、LogAppender六、Logger七、调试7.1调试步骤7.2尝试使用gdb调试 八、附录8.1log.h8.2log.cc8.3test.cc8.4Cmakelists.txt8.4Cmakelists.txt ​ 本篇文章主要针对一下sylar高性能服务器项目视频p1-p6的代码分析…

【数据结构】快排的详细讲解

目录&#xff1a; 介绍 一&#xff0c;递归快排确定基准值 二&#xff0c;递归遍历 三&#xff0c;非递归的快排 四&#xff0c;快排的效率 介绍 快排是排序算法中效率是比较高的&#xff0c;快排的基本思想是运用二分思想&#xff0c;与二叉树的前序遍历类似&#xff0c;…

玄子Share- IDEA 2023 Web 热部署

玄子Share- IDEA 2023 热部署 添加热部署依赖 普通 Web 项目使用热部署插件需指定版本 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><version>3.1.3</version><!-…

Spring Boot:自定义注解--annotation

目录 自定义注解的定义和作用范围如何创建自定义注解创建注解接口 如何使用自定义注解进行数据验证创建注解处理器控制器中使用注解 如何为字段添加注解 自定义注解的定义和作用范围 自定义注解可以作用在类、方法、属性、参数、异常、字段或其他注解上。 如何创建自定义注解…

ThreeJS-3D教学六-物体位移旋转

之前文章其实也有涉及到这方面的内容&#xff0c;比如在ThreeJS-3D教学三&#xff1a;平移缩放物体沿轨迹运动这篇中&#xff0c;通过获取轨迹点物体动起来&#xff0c;其它几篇文章也有旋转的效果&#xff0c;本篇我们来详细看下&#xff0c;另外加了tween.js知识点&#xff0…

MM-Camera架构-ProcessCaptureRequest 流程分析

文章目录 processCaptureRequest\_3\_41.1 mDevice1.2 mDevice->ops->process\_capture\_request1.3 hardware to vendor mct\_shimlayer\_process\_event2.1 mct\_shimlayer\_handle\_parm2.2 mct\_shimlayer\_reg\_buffer processCaptureRequest_3_4 sdm660的摄像头走…