单例模式——饿汉模式和懒汉模式

article/2025/9/22 1:58:11

目录

  • 🥝线程安全的单例模式
    • 🥝饿汉模式
    • 🥝懒汉模式
      • 🥝 懒汉模式总结

🥝线程安全的单例模式

线程安全的单例模式是面试中常见的问题,所以熟练掌握这种单例模式尤为重要
什么叫单例模式?
单例模式就是一种设计模式,写代码时一种常见的应用场景,设计模式就是针对于这些应用场景给出的解决方案,我们可以把它想成为棋谱,这种棋谱就是由那些“围棋高手”(大佬程序猿)留下来便于小白🥬使用
而且我们还需要知道单例模式的效果就是保证某个类只有唯一实例

单例模式分为饿汉模式和懒汉模式

🥝饿汉模式

顾名思义,饿汉模式就是表示比较着急的去干某件事,这里的饿汉并不是代表真的饿了,而是代表急不可待的意思
举个🌰 :小明假期留了三本大作业,结果放假第一天,小明就照着答案把这些作业全都写完了,于是小明的假期就变的甚是潇洒,显然,这种做法是不可取的,虽然速度提上去了,但是并没有什么卵用

我么可以引出饿汉的单例模式就是比较着急的去创建线程

下面我们就用代码来演示一下饿汉的单例模式

创建一个Singleton类(我们要知道singleton这个单词的意思,如果singleton大家不知道那么singledog的意思大家一定要知道,没错就是你“单身狗🐶 的意思”,那么singleton就是单个的意思),通过这个类来保证单例模式只有唯一的实例

*
线程安全的单例模式实现-饿汉模式*/class Singleton{//1.使用static创建一个实例,并且立即进行实例化,//这个instance对应的实例,是该类的唯一实例.private static Singleton instance = new Singleton();//2.为了防止程序猿在其他的地方不小心的new了这个Singleton,把构造方法设为privateprivate Singleton(){}//3.提供一个方法,让类外能拿到这个唯一实例public static Singleton getInstance(){return instance;}
}
public class TestDemo1 {public static void main(String[] args) {Singleton instance = Singleton.getInstance();}
}

上述代码中,static是用来修饰类成员(类属性/类方法),一个Java程序中,一个类对象只存在一份(JVM保证的),这样也就保证了static修饰的实例只有一份了
static是单例模式实现的主要方法

🥝懒汉模式

举🌰 :小红和小明是同班同学,小明为了能够愉快的过假期,一天的时间内写完了所有的作业,小红不同,小红是个好学生,她给自己定了个计划,每天写一点,而且很认真独立完成作业,在照着答案批改,写完作业后再去玩,就这样,小红用了一个假期写完了所有作业,开学后,小明受了老师的惩罚,小红却得到了表扬

写到这里,可能就会有同学会问,那为什么把这种模式叫做懒汉模式呢
我们需要知道,在计算机中是一种褒义词,并不是我们日常生活中的懒,
这种懒会让系统变得更高效,系统什么时候有时间,系统再去工作,而不是一股脑的把工作全给做完

我们可以看出懒汉模式的单例模式就是什么时候需要去做的时候,系统才回去创建实例,而不是一股脑的把实例全都创建出来

下面我们就用代码来演示一下懒汉的单例模式`

*
线程安全的单例模式实现-懒汉模式(只有真正的使用到getInstance的时候才真正的创建实例)*/
class Singleton2{//1.和饿汉模式有所不同,此模式并不是立即初始实例private static volatile Singleton2  instance = null;//2.将构造方法设为privateprivate Singleton2(){}//3.提供一个方法获取实例,但是只有真正需要的时候才会真正的去创造实例public static Singleton2 getInstance(){if(instance==null){instance = new Singleton2();}return instance;}
}

和饿汉模式有所不同,此模式并不是立即初始实例,而是判断一下是否真的需要,只有在真正的需要的时候才会去创建实例

以上是懒汉模式的雏形,但是,我们随着深入了解,会发现,懒汉模式存在许多问题

if(instance==null){instance = new Singleton2();}

这两行代码即包含了读操作,也包含了修改操作,而且这两行代码是两个步骤,不是原子性的(何为原子性,可以去了解我之前写的线程安全问题的博客),也就是说此代码存在线程安全问题

线程安不安全,具体的多线程环境下,并发的调用getInstance,是否存在BUG

请添加图片描述
如图解:我们不难看出,如果读操作和修改操作不是原子性的,就可能会导致多个线程创造出多个实例出来,就违背了我们的初衷,实现多线程的单例模式

所以,我们需要加锁操作:

synchronized(Singleton2.class){if(instance==null){instance = new Singleton2();}return instance;}}

上述代码使用了类对象作为了锁对象,类对象在程序中只存在一份,就能保证了多线程在调用getInstance的时候都是针对同一个对象进行的加锁

这样就保证了多操作和修改操作的原子性

加了锁之后线程变得安全了,但是又产生了新的问题

  1. 线程不安全是因为没有加锁,在加了锁之后,instance已经被初始化了,那么此时的instance就一定不是null了,这样代码就只会进行if和return的两个读操作,线程也就变的安全了,根据上述的代码,我么会发现,无论是在初始化前,还是在初始化之后,getinstance都是会被一直加锁的,如果instance已经被初始化了,那么在对getinstance进行加锁就会产生不必要的锁竞争

加锁虽然解决了线程安全问题,即开发效率,但是运行效率也随着降低,我们需要保证开发效率,也需要保证运行效率

改进方案:让instance初始化之前,才进行加锁,初始化之后就不需要加锁了,所以在加锁之前,加上一个判定条件,条件就是当前instance是否已经完成

 if(instance==null){synchronized(Singleton2.class){if(instance==null){instance = new Singleton2();}}}return instance;
}

有细心的同学可能会发现,上述两个if的判断条件是相通的,那么将所有的代码放在一个if条件里不就好了吗?
这是绝对不可以的!!!
因为代码如果是这样的

 if(instance==null){synchronized(Singleton2.class){instance = new Singleton2();}}return instance;

很明显,这样的代码没有保证读操作和修改操作的原子性,这也就代表着之前的所有铺垫都形同虚设了

那为什么会出现这种情况呢?这完全是一种美丽的巧合
1.第一个判定条件,是否需要加锁
2.第二个判定条件是否需要对instance初始化
这两个判定条件起到的效果/预期的目的是完全不一样的
碰巧这两个判定条件都是instance是否为null

在解决了上述代码后,还有最后一个重要的线程安全问题
我么发现,上述代码如果是多个线程共同调用这里的getinstance,因为这个方法只有一个(单例模式),就会导致方法会有大量的读操作产生,前面我们说到,如果代码有大量的读操作,编译器就会把这个读内存操作优化成读寄存器操作,这也就是我们所说的线程安全问题之一:内存可见性,这可能会引起第一个if判定条件产生误差,第二个不会产生影响,因为关键字synchronized,所以我们需要对instance这个变量进行修饰

  1. 使用volatile关键字

volatile可以保证变量不被编译器优化,但是不能保证原子性,此处我们只需要防止编译器优化即可

//1.和饿汉模式有所不同,此模式并不是立即初始实例private static volatile Singleton2  instance = null;//2.将构造方法设为privateprivate Singleton2(){}//3.提供一个方法获取实例,但是只有真正需要的时候才会真正的去创造实例public static Singleton2 getInstance(){if(instance==null){synchronized(Singleton2.class){if(instance==null){instance = new Singleton2();}}}return instance;}

🥝 懒汉模式总结

单例模式是我们秋招面试时非常重要的一个问题,懒汉模式尤为重要,懒汉模式产生的问题我们也需要熟练掌握

1. 在正确的位置加锁,保证读操作和修改操作的原子性
2. 双重if判定,避免产生不必要的锁竞争
3. 对instance使用关键字volatile,防止编译器优化

以上就是对线程安全中单例模式的总结,此案列非常重要,是多线程经典的案例,大家应该熟练掌握相关代码,最后可以写出来


http://chatgpt.dhexx.cn/article/gkWPO5tt.shtml

相关文章

C# 设计模式之单例模式(懒汉模式、饿汉模式、静态内部类模式)

C# 设计模式之单例模式(懒汉模式、饿汉模式、静态内部类模式) 应用场景:在整个软件运行生命周期内,一个类只允许一次实例化,例如数据库连接池的连接对象创建;通过使用单例模式来避免反复创建连接对象&#…

muduo源码剖析——Singleton单例模式之懒汉模式与DCL双重检查

0 懒汉与饿汉 对于Singleton单例模式我们并不陌生,但我们常用的多是饿汉模式: Singleton实例的声明和实例化在instance()函数中同时完成。而懒汉模式要求,Singleton实例的声明和实例化分开: 先声明Singleton实例对象&#xff0…

C++单例模式 : 懒汉模式 与 饿汉模式

单例模式: 只能有一个实例,有懒汉和饿汉区分,实现核心思想: 1.构造函数私有化 2.使用静态函数作为接口来获取类对象 1、懒汉模式: 由调用者实例,多线程情况下会存在线程安全问题,需要加互斥锁进…

单例模式的创建(饿汉模式懒汉模式)

🎈专栏链接:多线程相关知识详解 目录 一.什么是单例模式 二.用static来创建单例模式 三.饿汉模式与懒汉模式 四.饿汉模式与懒汉模式的线程安全问题 五.New引发的指令重排序问题 六.小结 一.什么是单例模式 单例模式就是指某个类有且只有一个实例(instance…

单例模式:懒汉模式

所谓“懒汉式”与“饿汉式”的区别,是在与建立单例对象的时间的不同。 “懒汉式”是在你真正用到的时候才去建这个单例对象“饿汉式是在类创建的同时就已经创建好一个静态的对象,不管你用的用不上,一开始就建立这个单例对象 代码实现&#x…

java 单例模式 之懒汉模式

单例模式:一个类,始终仅仅对外提供自己的一个实例,这样的设计方案,就称单例模式。 懒汉模式: 构造函数私有 声明私有的本类静态实例 定义静态的方法,在方法中创建本类实例,并返回该实例 pu…

单例模式饿汉模式与懒汉模式

目录 1.什么是单例模式 2.为什么需要单例模式? 3.如何实现单例模式 3.1饿汉方式 3.2懒汉模式 1.什么是单例模式 单例模式是一种设计模式,单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例。单例模式的具体实现又分为饿汉模式…

关于Java中单例模式(饿汉模式和懒汉模式)的简析

目录 一.什么是单例模式 二.饿汉模式和懒汉模式 饿汉模式 代码 懒汉模式 代码 关于多线程安全的问题 如何解决懒汉模式多线程安全问题 双if判断 一.什么是单例模式 简单来说,就是我们在程序中通过代码进行限制,在该程序中 只能创建一个对象 二.饿汉模式和懒汉模式 …

java设计模式之单例模式|单例模式之饿汉模式、懒汉模式、枚举方式|最详细的6种懒汉模式详解

目录 一、单例模式 二、饿汉模式和懒汉模式 1、饿汉模式,线程安全 2、懒汉模式 懒汉模式1,线程不安全(不常用) 懒汉模式2,线程安全(不常用) 懒汉模式3,线程安全,双…

全志F1C100s主线linux入坑记录 (10)调试串口更改

调试串口更改 百度网站 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 调试串口更改前言uboot 修改一、修改设备树二、修改文件3. 修改内核传递参数 内核修改参考 前言 未完成版本 未完成版本 未完成版本 未完成…

f1c100s 调试问题汇总

问题 usb无法识别 windows显示无法识别的usb设备 解决: 卸载设备,插拔一下,就可以识别了,之后就会自动安装驱动。 umount失败 fuser ./d2 可以显示出当前哪个程序在使用磁盘上的某个文件、挂载点、甚至网络端口,并…

【f1c200s/f1c100s】FT5426触摸屏驱动适配

【f1c200s/f1c100s】FT5426触摸屏驱动适配 前言设备树配置IIC控制器FT5426设备树配置 内核配置结果 前言 嵌入式linux下的触摸屏驱动是基于input子系统的,当触摸发生时,内核上报触摸事件至用户层。我使用的显示屏是正点原子的7寸RGB接口显示屏&#xff…

f1c100s开发笔记

f1c100s开发笔记 全志芯片相关的论坛帖f1c100s移植帖交叉编译器的安装uboot的编译适配配置开始编译uboot编译遇坑 2020-05-20 09:56:15 星期四 全志芯片相关的论坛帖 https://whycan.cn/t_3019.html#p25005 f1c100s移植帖 https://whycan.cn/t_3211.html 交叉编译器的安装 …

全志F1C100S/F1C200S学习笔记(1)——基础简介及资料

文章目录 一、芯片概览二、芯片框图三、芯片规格四、资料:五、仓库:一、芯片概览 二、芯片框图 三、芯片规格 功能描述CPUARM9 CPU architecture16KByte D

f1c100linux系统吗,全志F1C100s怎么样 F1C100s芯片参数介绍

全志F1C100s芯片怎么样,F1C100s处理器好用吗?F1C100s是720P高清多媒体处理器。下面带来F1C100s芯片的具体参数,准备入手搭载F1C100s芯片设备的用户可以参考一下。 F1C100s芯片架构图 F1C100s特性介绍 支持H.264 1920x108030fps 解码 支持MJPE…

全志F1C100S的BROM研究

全志f1c100s是个性价比很高的芯片,但是对一般人不太友好的是它的资料开放的太少了。 网上找不到完整版的用户手册,只能从有限的手册文档和参考代码旁敲侧击,反向猜测。 关于它的BROM网上的手册内容很少。 手册上只有短短3句话: 具…

10、Lctech Pi(F1C200S)驱动电阻屏触摸芯片ns2009(ts2007),buildroot配置tslib(CherryPi,Mangopi,F1C100S)

本次主要参考: https://github.com/mangopi-sbc/buildroot-mangopi-r https://blog.csdn.net/qq_35031421/article/details/113436888 https://blog.csdn.net/dancheqishi23/article/details/116498088 (如果方便请给这几位大佬一个关注) 开…

F1C100S自制开发板调试过程

疫情,等了好久板子终于到了。 我这里使用的是坑网大佬提供的tiny200开发包,用的芒果派R3配置文件 1,配置其的介质,我板子上用的是nor-spi-flash,所以需要在设备树里面屏蔽掉nand-flash相关的节点,否则启动会有错误。 …

F1C100S(Lichee Nano)触摸屏 (GT9147)

1、前提 Ubuntu 环境版本 (18.04) Linux ubuntu 5.4.0-131-generic #147~18.04.1-Ubuntu SMP Sat Oct 15 13:10:18 UTC 2022 x86_64 x86_64 x86_64 GNU/LinuxARM GCC版本 gcc version 7.2.1 20171011 (Linaro GCC 7.2-2017.11)F1C100S Linux版本 (linux-nano-5.2-tf) 链接 …

全志F1C100s主线linux入坑记录 (5)LVGL8.2移植

LVGL8.2移植 百度网站 文章目录 LVGL8.2移植一、安装VScode二、安装lvgl模拟器二、F1c100s 移植lvgl参考 一、安装VScode 进入VScode官网下载安装包,如果最新版本安装有问题可以安装老版本的 https://code.visualstudio.com/使用命令行安装VScode sudo dpkg -i …