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

article/2025/9/22 1:59:13

🎈专栏链接:多线程相关知识详解

目录

一.什么是单例模式 

二.用static来创建单例模式

三.饿汉模式与懒汉模式

四.饿汉模式与懒汉模式的线程安全问题

五.New引发的指令重排序问题 

六.小结


一.什么是单例模式 

单例模式就是指某个类有且只有一个实例(instance)

这个是由需求决定的,有些需求场景就要求实例不能有多个,通过单例模式,相当于对'单个实例'做了一个更加严格的约束

单例模式本质上就是通过借助编程语言的特性,强行限制某个类,不能创建多个实例

 二.用static来创建单例模式

static修饰的成员/属性就变成了类成员/类属性,当属性变成类属性的时候,就是'单个实例'了,例如:

public class Demo{static String str = "";
}

更具体的说是类对象的属性,而类对象是通过JVM加载.class文件来的,此时类对象,其实在JVM当中也是'单例'

即JVM针对某个.class文件只会加载一次,也就只有一个类对象,类对象上面的成员(即static修饰的)也就只有一份了,在整个进程中都是独一无二的

class Singleton{private static Singleton instance = new Singleton();public static Singleton getInstance(){//该方法能让类外面获取到唯一的'实例'return instance;}//把构造方法设置为私有的,此时在类的外面就无法继续 new 一个新的实例了private Singleton(){};
}

在该单例类外面获取到单例的正确做法

Singleton instance = Singleton.getInstance();

 三.饿汉模式与懒汉模式

上面所写的代码实现单例模式的方式就叫做"饿汉模式" ,因为程序运行时用到了这个类就进行加载,这个实例在类加载阶段就创建好了,创建的时机非常早

还有另一种创建单例模式的方式叫做"懒汉模式",相比于"饿汉模式"创建实例的时机更迟,带来了更高的效率

class SingletonLazy{private static SingletonLazy instance = null;public static SingletonLazy getInstance(){//首次调用getInstance才会触发,后续调用getInstance发现instance不为空了,就不会再创建实例,立即返回instanceif(instance == null){instance = new SingletonLazy();}return instance;}private SingletonLazy(){}
}public class Demo2 {public static void main(String[] args) {SingletonLazy instance = SingletonLazy.getInstance();}
}

如果后面没人调用getInstance,这样就把构造实例的过程给节省下来了,效率也就提升了

或者即使有代码后面调用getInstance,但是调用的时机就比较晚,创建实例的时机也就迟了,能够和其他的耗时操作岔开了(一般程序刚启动跑起来的时候,要初始化的东西比较多,系统资源比较紧张),而饿汉模式刚开始就创建实例,与其他所需要初始化的东西竞争,使系统资源雪上加霜

所以使用懒汉模式比使用饿汉模式创建单例模式更好 

四.饿汉模式与懒汉模式的线程安全问题

虽然懒汉模式创建单例模式更好,但还要考虑线程安全问题,即多个线程并发运行的时候

饿汉模式属于线程安全,而懒汉模式属于线程不安全

在饿汉模式的 getInstance 方法里面并没有关于修改的操作,只涉及到读操作,所以饿汉模式是线程安全的

而懒汉模式的 getInstance 方法里面既涉及到读操作也涉及到修改操作,线程就不安全了

在有多个线程的时候,例如线程1 LOAD 完进行 CMP 发现 instance 为空就NEW一个新的实例,但线程二在线程1 LOAD 完后也 LOAD 然后线程1 CMP 完线程2也 CMP ,此时线程1还没进行修改操作,所以线程2也判断 instance 为null,然后也就NEW了一个实例,这样就会导致实例被创建出来了多份

如何才能让懒汉模式线程安全?

就是让以上的操作变成原子的,即加锁

public static SingletonLazy getInstance(){synchronized (SingletonLazy.class){//在读之前加上一把锁if(instance == null){instance = new SingletonLazy();}}//修改完毕后解锁return instance;}

该线程不安全的时候只是在实例创建之前(首轮调用的时候)才会触发线程不安全的问题,在实例创建好之后if条件就进不去,就只变成读操作,但是仍然会频繁的加锁和解锁,就会产生很大的开销,可能涉及到用户态和内核态之间的切换,代价比较大

所以需要判断什么时候需要加锁,什么时候不需要加锁,

实例创建之前,线程是不安全的,就需要加锁,实例创建之后,线程是安全的,就不需要加锁

因此可以在外面加上一层判断条件

    public static SingletonLazy getInstance(){if(instance == null){//判断是否要加锁synchronized (SingletonLazy.class){if(instance == null){//判断是否要创建实例instance = new SingletonLazy();}}}return instance;}

因为两个判断之间隔着一个加锁操作,加锁可能导致锁竞争,锁竞争就会导致阻塞,不知道什么时候能够唤醒,所以第一个if和第二个if的结果可能是截然不同的,即第一个if成立了,第二个if不一定成立

五.New引发的指令重排序问题 

new操作本质上分为3个步骤:

①申请内存,获取内存首地址

②调用构造方法,来初始化实例

③ 把内存的首地址赋值给instance引用

单线程的情况下,步骤②和步骤③的执行的先后顺序没什么差别,就可能发生指令重排序,执行的顺序为①③②

多线程的情况下,第一个线程的执行顺序为①③②,在步骤③执行结束之后,步骤②执行开始之前,第二个线程调用了getInstance方法,这个方法就会认为instance的值不为null,那么就直接返回 instance 实例,并对instance进行解引用操作(使用里面的属性/方法),但此时线程1的步骤②其实还没执行,instance的值还没有初始化仍然为空,线程2调用了就会出现问题

例如:老师布置一份作业说明天早上之前写完,你没写完,第二天早上以为不会上交,跟老师讲我写完了,老师突然说拿给他看看,你拿不出来,这不就坏事了,这个情况跟指令重排序带来的问题是一样的.

那么如何禁止发生指令重排序呢?就是使用volatile来禁止

private volatile static SingletonLazy instance = null;//volatile用来禁止指令重排序

就是直接给instance加上volatile禁止有关instance的操作发生指令重排序

六.小结

创建线程安全的单例模式有三个要注意的点:

1.加锁将读写操作变为原子的

2.双重if避免不必要的加锁解锁

3.加volatile防止有关实例的操作指令重排序

 


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

相关文章

单例模式:懒汉模式

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

全志F1C100s主线linux入坑记录 (7)GBA模拟器移植

GBA模拟器移植 百度网站 文章目录 GBA模拟器移植一、下载gpsp 源代碼二、gpsp环境配置参考 一、下载gpsp 源代碼 gpsp源码 解压文件 7z x gpsp.7z 二、gpsp环境配置 gpsp需要SDL环境我们先在bulidroot中添加SDL包 make menuconfigTarget packages ---> Graphic libra…

9、Lctech Pi(F1C200S)开启I2C0(CherryPi,Mangopi,F1C100S)

本次主要参考: https://github.com/mangopi-sbc/buildroot-mangopi-r https://wiki.sipeed.com/soft/Lichee/zh/Nano-Doc-Backup/index.html (如果方便请给这几位大佬一个关注) 注意代码块之间的空行 配置设备树文件 1、打开linux-5.7.1/a…

全志F1C100s主线linux入坑记录 (3)适配其他分辨率的LCD

适配其他分辨率的LCD 百度网站 文章目录 适配其他分辨率的LCD一、修改U-boot屏幕参数二、修改linux内核文件三、测试效果 一、修改U-boot屏幕参数 修改对应屏幕的参数(我这里是1024*600的屏幕) 修改 -> ARM architecture -> Enable graphical ub…

全志F1C100S从零开发记录(1)

1.刚买到板子,开箱如下图所示: 2.管脚焊接(焊接串口,用来看串口打印信息),5v供电: 3.通过usb转ttl接到电脑上: . 4.设置串口波特率115200 ,打开串口,上电出…