当前位置:网站首页>【多线程】 实现单例模式 ( 饿汉、懒汉 ) 实现线程安全的单例模式 (双重效验锁)
【多线程】 实现单例模式 ( 饿汉、懒汉 ) 实现线程安全的单例模式 (双重效验锁)
2022-07-01 18:50:00 【51CTO】
目录
单例模式
什么是单例模式
要求我们代码中的某个类,只能有一个实例,不能有多个实例。实例就是对象。就是说某个类只能new 一个对象,不能new多个对象。
这种单例模式,在实际开发中是非常常见的,也是非常有用的。
开发中的很多“概念”,天然就是单例的。
比如:
使用JDBC操作数据库,此时数据库连接可以通过数据库连接池数据库连来获取
连接池就可以使用单例模式,创建唯一的一个对象.大部分跟数据有关的东西,服务器里面只存一份。那么,就都可以使用“单例模式”来进行表示。
单例模式的两种经典实现
单例模式中有两个典型实现:
饿汉模式懒汉模式
我们来通过一个生活上的例子来给大家讲讲什么是饿汉模式,什么是懒汉模式。
洗碗:
第一种情况:
假设我们中午吃饭的时候,一家人用了4个碗。然后吃完之后,马上就把碗给洗了
这种情况,就是饿汉模式
第二种情况:
中午吃饭的时候,一家人用了4个碗。然后吃完之后,碗先放着,不着急洗
等待晚上吃饭的时候,发现只需要2个碗。
那么就将 4个没洗的碗 中,洗出2个碗,拿来用。吃完之后,碗先放着,不着急洗。
如果下一顿只用一个玩,就洗出1个碗就是用多少,拿多少。少的不够,多的不要
这就是懒汉模式
但是在计算机中,普遍认为 懒汉模式 比 饿汉模式好。主要因为 懒汉模式 的效率更高
也很好理解:洗 2 个 碗,肯定比洗4个碗轻松。
所以用几个洗几个。
根据需要,进行操作。“懒” 这个字一般 在计算机中,是一个褒义词。
1. 饿汉模式 (线程安全)
饿汉模式的类在类被加载的过程中就会立刻实例化一个对象所以后续无论如何操作 只要严格使用get方法 就不会出现其他的实例- 由上可知
饿汉模式是线程安全的他也会有一个问题就是即使我不使用这个类 他还会创建一个实例来占用我们的内存 这就导致他的效率就不高这个版本的单个实例不能有其他实例变量, 不然还是会出现非线性安全问题(非线程安全问题就是有个线程对用一个实例变量修改造成数据的脏读)

分析:
(1) 为什么是线程安全的?
由于在类加载的时候就初始化了(第2行),只有一份,所以是线程安全的.(2) 缺点
①还没有使用,就浪费了内存空间.
②new对象(没有执行类加载,会先执行,再执行成员变量+实例代码块+构造方法),可能抛异常,也就是还没有调用getInstance,在类加载时就抛出了异常,那么以后调用getInstance方法时,都是会出现问题的.
2. 懒汉模式 (线程不安全)
懒汉模式就改进了饿汉模式的缺点 他只有在使用的时候才会让该类去实例化一个对象并且此后再去获取对象只能获取这一个对象- 所以我们一般认为懒汉模式比饿汉模式
效率更高- 但是
懒汉模式也有缺点他线程不安全这点在可以优化解决
由于比较懒,你让我干活,我才开始,否则我不会主动干的~
说人话就是: 类加载时,不急着初始化对象,第一次调用,初始化对象,后边再次调用,直接返回第一次创建好的对象.
分析:
为什么是线程不安全的?
在
getInstance()方法中,总共有三行Java代码。
而且其中instance = new Singleton() 会被分解成三行字节码指令,相当于并发并行的对多行共享变量的操作,所以是线程不安全的.

区别:
饿汉模式 和 懒汉模式 的唯一区别:
在于 创建实例的时机不一样。
饿汉模式 是 类加载时,创建。懒汉模式 是 首次使用时,创建。所以懒汉模式就更懒一些,不用的时候,不创建;等到用用的时候,再去创建。
这样做的目的,就是节省资源。
其实在计算机很多其它场景中,也会涉及这情况。
一个典型的案例:
notepad 这样的程序(记事本软件),在打开大文件的时候是很慢的。
假如,你要打开一个 1G 大小的文件,此时 notepad 就会尝试把这 1 G 的 所有内容都读到内存中。
将 1G 的数据量 存入 内存,显然是非常慢的。
不管你要不要,全部都给你。
这就是饿汉模式。问题也随之而来:这些数据,我们真的能全部用得到吗?显示是不太可能的。
因此就会浪费很多资源。
像一些其他的程序,在打开大文件的时候就有优化。
假设也是打开 1G的文件,但是只先加载这一个屏幕中能显示出来的部分。看到哪,加载到哪里。这样不会用空间上的浪费
这就是懒汉模式
实现线程安全的单例模式
1. 懒汉模式+synchronized静态同步方法 (线程安全但效率差)
说到让一个代码线程安全,我们自然而然的就想到加锁!
但是问题就在于:在哪个地方加锁合适呢?
其实也很好观察,将 if 语句的执行操作 给 加锁,使其两个操作为原子性。
直白来说: 就是 if 语句 打包成“一个整体”,就跟前面分析 count++ 一样。
一致性执行完。

加锁范围 一定要包含 if 语句!!!
要不然没有效果,就像下面这样!
本来我们是想将 读 和 写 操作,打包成一个整体,
但是现在只是 针对写操作进行加锁,这时候就跟没加锁 一样,是没有区别的。
请大家注意!并不是代码中有 synchronized,一定就是线程安全的。这需要看 synchronized 加的位置,也要正确。所以 synchronized 写的位置。不能随便。
回过头来,我们再来看一下 synchronized 锁的对象写我们应该些什么。

虽然我们确实通过上述加锁操作,解决了 if 语句 的原子性问题。
但是!这样的程序,还存在这几个问题!
1.代码执行效率问题
2、指令重排序
虽然其他线程再调用 单例线程的时候,也是加了 synchronized 的。
减缓了循环速度,从而保证了 内存可见性。
但是!还有一个问题,来看下面。
此时,我们才完成一个线程安全的单例模式 - 懒汉模式
1、正确的位置加锁2、双重if判定3、volatile关键字
2. 懒汉模式+二次判断(双重校验锁,线程安全且效率高)
用法:
- 双重校验: 两个if.
- 锁: synchronized
- 注意使用静态变量(引用) 使用volatile.
分析:
- ①对象初始化前,多线程调用getInstance(),需要保证线程安全,即执行第5,6,7,8行.
- ②对象初始化以后,只执行 if 判断和 return 语句,即只执行第5,9行.
② 明显比 ① 执行的情况多很多,所以考虑不加锁,提高效率.
② 不需要加锁,可以使用volatile保证可见性.
即:
第5行: 初始化完成之后,不需要加锁,使用volatile修饰变量,保证可见性,能满足线程安全,代码行本身就是原子性. 可以并行并发的执行,提高了效率.
第6行: 没有初始化完成时,创建对象需要加锁来保证线程安全.
第7行: 竞争锁失败的线程,还会执行同步代码块,需要再次判断,保证只初始化一次.
第9行: 引用使用了volatile关键字,还有建立内存屏障,禁止指令重排序的功能(new 分解的三条指令: 分配内存空间,初始化对象,赋值给变量)
边栏推荐
- 优质笔记软件综合评测和详细盘点(一) Notion、Obsidian、RemNote、FlowUs
- Tensorflow reports an error, could not load dynamic library 'libcudnn so. eight
- Win11快捷键切换输入法无反应怎么办?快捷键切换输入法没有反应
- mysql 报错 Can‘t create table ‘demo01.tb_Student‘ (errno: 150)*
- 毕业季 | 华为专家亲授面试秘诀:如何拿到大厂高薪offer?
-
- Object creation
- 1592 example 1 King (sgu223 loj10170 luogu1896 increase + / provincial election -) violent thinking pressure DP 01 Backpack
- STC 32位8051单片机开发实例教程 二 I/O工作模式及其配置
- Shell advanced
猜你喜欢
![[research materials] Huawei Technology ICT 2021: at the beginning of the](/img/83/f21f148103815691796f87a95c687f.jpg)
[research materials] Huawei Technology ICT 2021: at the beginning of the "Yuan" year, the industry is "new" -- download attached

通过js实现金字塔(星号金字塔,回文对称数字金字塔)
![[research data] observation on the differences of health preservation concepts among people in 2022 - Download attached](/img/50/926cc5bce83f8b195b3e2072b656bf.jpg)
[research data] observation on the differences of health preservation concepts among people in 2022 - Download attached

servlet知识点

Oracle物理体系结构

Basic use of MySQL

开发那些事儿:EasyCVR集群设备管理页面功能展示优化

STC 32位8051单片机开发实例教程 二 I/O工作模式及其配置

再回顾集合容器

JS 之 常用内置类的使用
随机推荐
STC 32位8051单片机开发实例教程 二 I/O工作模式及其配置
Object creation
对象的创建
MySQL signale une erreur can 't create table' demo01. TB Étudiant '(errno: 150)
list分割成满足和不满足条件的集合(partitioningBy)
Procédure de mesure du capteur d'accord vibrant par le module d'acquisition d'accord vibrant
JS 之 常用内置类的使用
MySQL reports an error can't create table 'demo01 tb_ Student‘ (errno: 150)*
DS transunet: Dual Swing transformer u-net for medical image segmentation
Hls4ml reports an error the board_ part definition was not found for tul. com. tw:pynq-z2:part0:1.0.
Review the collection container again
New window open page -window open
对金额进行求和
How to use console Log print text?
Cookie和Session的相关概念
Unreal Engine packaging project
Stack Overflow 2022 开发者调查:行业走向何方?
1592 example 1 King (sgu223 loj10170 luogu1896 increase + / provincial election -) violent thinking pressure DP 01 Backpack
PowerDesigner设计Name和Comment 替换
SQL getting started plan-1-select

