设计模式———单例模式

news/2024/5/17 19:28:35

单例也就是只能有一个实例,即只创建一个实例对象,不能有多个。

可能会疑惑,那我写代码的时候注意点,只new一次不就得了。理论上是可以的,但在实际中很难实现,因为你无法预料到后面是否会脑抽一下~~因此我们就可以通过编码技巧,让编译器来帮助我们检查约束。

单例模式又分为两种:饿汉式和懒汉式区别在于实例对象创建的时机不同

0x00 饿汉式

说到饿汉,那一定是很饥饿的人,很急切,也就是说这个实例对象创建的时机比较早,在类加载的时候就创建了。

要点

  • 1、由于创建时机较早,设置为静态即可在类加载的时候就可以创建实例对象

  • 2、由于只能创建一个实例对象,因此将构造函数私有化

  • 3、对外提供一个获取实例对象的静态类方法

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

0x01 懒汉式

懒汉,顾名思义,很懒,比较缓慢,也就是说这个实例对象是在使用到的时候才去创建。

要点

  • 1、一开始设置为null,直到在使用的时候再去new实例对象

  • 2、由于只能创建一个实例对象,因此将构造函数私有化

3、对外提供一个获取实例对象的静态类方法

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

0x02 单例模式与多线程

众所周知,许多在单线程环境下的代码一到多线程环境下就会出错,在此将对单例模式在多线程下的编写进行调整优化~

先来看看饿汉式的代码,我们发现这个实现的方式是在类加载的时候就去实例化对象了,而后续在多线程中掉用getInstance方法来获取实例对象的时候,只会进行返回操作,即”只读”因此多个线程在操作的时候是安全的

再来看看懒汉式,可以发现在调用getInstance方法的时候,会进行修改操作,如果恰巧有两个线程同时进入了if,然后就会进行new操作,那不就创建了多个实例对象了,如果恰巧每个对象不是很轻量,可能有很多属性,加载了几十G的数据到内存中......

因此解决方法是进行加锁操作,保证if和new操作是原子的

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

这样修改以后,就能保证if和new操作是一个整体,此时线程的安全问题就得到了改善。

但是这样写是否就真的完了呢?

答案:不是。因为我们是单例模式,所以只需要new一次之后,实例对象就不可能是null了,后续直接return对象就行了。假如我们现在已经创建好了一个对象,当后续有多个线程去调用getInstance方法的时候,需要去获取到这把锁才能进行返回操作,也就是我们的多线程变成了串行化,并发程度几乎没了~

那是否有办法,既可以保证代码线程安全,又不会对执行效率产生大的影响呢?

如下代码:

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

与前面的代码对比,可以发现,在外层多加了一个if语句。

分析

当有多个线程进入了第一个if语句,此时里面有一个加锁操作,这样可以保证只创建了一个实例对象。假设此时已经创建好了一个实例对象了,当下一次又有多个线程进来时,由于instance不为null,直接返回了,并不会进入到加锁操作,此时并发并没有再受到影响了,既保证了正确又保证了效率。

总结

在锁外面的if语句保证了是否要进行加锁操作,即如果还没有创建实例对象,此时线程由于需要进行修改操作要进行加锁才能保证安全,如果已经创建了实例对象,此时线程安全了就不用加锁了。

在锁里面的if语句是来保证只创建一个实例对象。

0x03 指令重排序

上述代码其实还不够安全,还存在指令重排序的问题。

指令重排序,本质是编译器为了提高执行效率,调整原有的代码执行顺序(前提是保持逻辑不变)。

接下来具体问题具体分析:

上述的代码中,出现了一个new操作,而new操作其实是分三步的,第一步,申请内存空间,第二步,在内存空间上构造对象,第三步,把内存地址赋值给对象引用。一般是按照1,2,3的顺序执行的,但是编译器优化过后,可能会按照1,3,2的顺序执行,在单线程环境下都可以,但是在多线程环境下则不是。如果线程1是按照1,3,2顺序执行完了3后,线程2此时返回了这个实例对象,但是此时这个对象还没有初始化呢,也就是说这是个非法对象,当我们的线程2去访问这个对象的属性或方法时,此时就是非法,会出错。此时我们还需要再对instance加一个限制操作,即用volatile来修饰instance。

最终代码

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

至此,这段“健壮”的代码基本可以满足使用了~


http://www.mrgr.cn/p/38200204

相关文章

Codes 重新定义 SaaS 模式的研发项目管理平台开源版 4.5.3 发布

Codes 是一个 开源、高效、简洁、轻量的一站式研发项目管理平台。包含需求管理,任务管理,测试管理,缺陷管理,自动化测试,cicd 等功能; Codes 重新定义 SaaS 模式 = 云端认证 + 程序及数据本地安装 + 不限功能 + 30 人免费一:简介Codes 重新定义 SaaS 模式 = 云端认证 …

【微信小程序】解决分页this.setData数据量太大的限制问题

1、原始方法,每请求一页都拿到之前的数据concat一下后整体再setData loadData() {let that thislet data {}data.page this.data.pagedata.size this.data.sizefindAll(data).then(res > {if (res.data.code 1) {this.setData({dataList: this.data.dataLi…

synchronization(同步)

并发进程之间的关系在内存中同时存在的若干个进程/线程,由操作系统的调度程序采用适当的策略将他们调度至cpu上运行,同时维护他们的状态队列。多个并发进程/线程从宏观上是同时在运行; 从微观的角度看,他们的运行过程是走走停停; 并发的进程/线程是交替执行(Interleaving…

修改中文、英文参考文献在文末列表中的顺序:EndNote

本文介绍在EndNote软件中,使得参考文献按照语种排列,中文在前、英文在后的方法~本文介绍在EndNote软件中,使得参考文献按照语种排列,中文在前、英文在后的方法。前期我们在EndNote参考文献格式Output Styles界面介绍一文中,详细介绍了文献管理软件EndNote的引用格式自定义…

网络编程ServerSocketChannel

ServerSocketChannel 1 非阻塞 vs 阻塞1.1 阻塞1.2 非阻塞1.3 多路复用 2 Selector2.1 创建2.2 绑定 Channel 事件2.3 监听 Channel 事件2.4 💡 select 何时不阻塞 3 处理 accept 事件💡 事件发生后能否不处理 4 处理 read 事件4.1 💡 为何要…

蓝桥杯2024年第十五届省赛真题-宝石组合

思路:参考博客,对Ha,Hb,Hc分别进行质因数分解会发现,S其实就等于Ha,Hb,Hc的最大公约数,不严谨推导过程如下(字丑勿喷): 找到此规律后,也不能枚举Ha&#xff…

SURE:增强不确定性估计的组合拳,快加入到你的训练指南吧 | CVPR 2024

论文重新审视了深度神经网络中的不确定性估计技术,并整合了一套技术以增强其可靠性。论文的研究表明,多种技术(包括模型正则化、分类器改造和优化策略)的综合应用显着提高了图像分类任务中不确定性预测的准确性 来源:晓飞的算法工程笔记 公众号论文: SURE: SUrvey REcipes…

基于face_recognition实现的人脸识别功能

环境Python 3.11.8 dlib == 19.24.4 opencv-python == 4.9.0.80 numpy == 1.26.4 face_recognition == 1.3.0通过本地图片采集人脸编码 import os import cv2 import face_recognition encode_list = [] image_field_path = os.path.join(., images) images_file_list = os.lis…

Spring Boot 目前还是最先进的吗?

当谈到现代Java开发框架时,Spring Boot一直处于领先地位。它目前不仅是最先进的,而且在Java生态系统中拥有着巨大的影响力。 1. 什么是Spring Boot? Spring Boot是由Spring团队开发的开源框架,旨在简化基于Spring的应用程序的开…

4.10 + (double)(rand()%10)/100.0

机房是我家黑色星期四 坏消息: 没有奥赛课,所以大概率调不出来 CF1479D 好消息: 5k 回来了,调题有望 🥰 中午起床直接来的机房,有学科自习就说我不知道 结果被叫回去了 😢 而且今天班里没水了,趁着大课间跑操又去了一趟机房,赢 奥赛大会 老规矩颁奖典礼打头 不一样的…

RAG 2.0架构详解:构建端到端检索增强生成系统

关于检索增强生成(RAG)的文章已经有很多了,如果我们能创建出可训练的检索器,或者说整个RAG可以像微调大型语言模型(LLM)那样定制化的话,那肯定能够获得更好的结果。但是当前RAG的问题在于各个子模块之间并没有完全协调,就像一个缝合怪一样,虽然能够工作但各部分并不和…

东方博宜 1157. 最小数

东方博宜 1157. 最小数 今天不想写思路&#xff0c;乱糟糟的&#xff0c;能运行就拉倒了 #include <iostream> using namespace std; int main() {int n ;int a[201] ;cin >> n ;for(int i 1 ; i < n ; i){cin >> a[i] ; } int j ;j 1 ;for(int i 1…

Java对接第三方接口C#语言 请求是xml格式方式

文章目录 目录 文章目录 安装流程 小结 概要写法流程技术细节小结 概要 实现方式通过标签方式获取一个Body内标签的信息一步一步解析到需要获取到的数据信息 写法流程 技术细节 先和对面对接项目的开发拿到postman接口数据信息&#xff0c;然后再本地跑通接口&#xff0c;再进…

荣誉

荣誉 个人 学习委员 国防教育先锋队 朋辈导师 优秀团员 省级三好学生 职业技能大赛网络安全 河南省第七届御网杯信息安全大赛三等奖第二十届全国大学生信息安全对抗技术竞赛 ​ 数通 H3C认证路由交换网络工程师.pdf H3CSE-RS-IPv6.pdf 华为ICT网络赛道三等奖华为 HarmonyOS应用…

Windows10中多屏显示器型号获取并与Screen对应

需求:标识某块屏,不参与窗口快速移动 @@@codepublic class Monitor{/// <summary>/// DeviceID,如: \\.\DISPLAY17/// </summary>public String DeviceName { get; set; } /// <summary>/// 名称,如: Default_Monitor/// </summary>public…

制作适用于openstack平台的win10镜像

1. 安装准备 从MSDN下载windows 10的镜像虚拟机开启CPU虚拟化的功能。从Fedora 网站下载已签名的 VirtIO 驱动程序 ISO 。 创建15 GB 的 qcow2 镜像&#xff1a;qemu-img create -f qcow2 win10.qcow2 15G 安装必要的软件 yum install qemu-kvm qemu-img virt-manager libvir…

ubuntu20 解决网线不能联网 RTL8111/8168/8411

这种问题一般是驱动没有正确安装。 ----RTL8111/8168/8411是一块比较坑的网卡。 1、 查看网卡信息 lspci |grep Ethernet2、 对于高版本的Ubuntu&#xff0c;能直接使用命令安装驱动。下面的r8168-dkms需根据网卡信息修改&#xff0c;上面的网卡信息还有8111&#xff0c;但逐个…

【云原生】Spring Cloud微服务学习路线汇总

【云原生】Spring Cloud微服务学习路线汇总Spring Cloud是什么?简单来说Spring Cloud是一系列框架的组成集合。主要利用的我们现在主流应用的Spring Boot框架开发便利性、巧妙地简化了分布式系统基础设施的开发,如服务发现注册、配置中心、消息总线、负载均衡、断路器、数据监…

ROS笔记5--动作通讯

1、动作通讯简介 机器人是一个复杂的智能系统,并不仅仅是键盘遥控运动、识别某个目标这么简单,我们需要实现的是送餐、送货、分拣等满足具体场景需求的机器人。在这些应用功能的实现中,另外一种ROS通信机制也会被常常用到——那就是动作。 从这个名字上就可以很好理解这个概…

c# 中 dataGridView控件 显示水平滚动条

1. 最主要的在dataGridView控件属性中的ScrollBars是否设为BothBoth代表水平和垂直方向根据实际需求自动显示滚动条None 代表水平和垂直都不显示滚动条Vertical 代表只垂直显示滚动条Horizontal 代表只水平显示滚动条 2.检查表格中每个列的属性,看 Frozen 应设置为 false 如果…