【多线程】多线程案例——单例模式详解(包含懒汉模式,饿汉模式)

news2025/4/6 11:09:02

单例模式

  • 🌴饿汉模式
  • 🎍懒汉模式
    • 🌸单线程版(线程不安全)
    • 🌸多线程版
    • 🌸多线程版(改进)
  • ⭕总结

单例模式是校招中最常考的 设计模式之⼀.

啥是设计模式?

  • 设计模式好⽐象棋中的 “棋谱”. 红⽅当头炮, ⿊⽅⻢来跳. 针对红⽅的⼀些⾛法, ⿊⽅应招的时候有⼀ 些固定的套路.按照套路来⾛局势就不会吃亏.
  • 软件开发中也有很多常⻅的 “问题场景”. 针对这些问题场景, ⼤佬们总结出了⼀些固定的套路. 按照这个套路来实现代码, 也不会吃亏.

单例模式能保证某个类在程序中只存在唯⼀⼀份实例, ⽽不会创建出多个实例.
啥是单例模式?

  • 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
  • 这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
  • 单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供了一个全局访问点来访问该实例。

注意

  1. 单例类只能有一个实例。

  2. 单例类必须自己创建自己的唯一实例。

  3. 单例类必须给所有其他对象提供这一实例

单例模式具体的实现⽅式有很多. 最常⻅的是 “饿汉” 和 “懒汉” 两种.

🌴饿汉模式

类加载的同时, 创建实例.(类加载的时候就会创建类实例)
代码示例

class Singleton {
// instance被static 修饰
 private static Singleton instance = new Singleton();
// 构造方法被private修饰,不能通过构造方法获取类实例
 private Singleton() {}
//只能通过getinstance方法获取唯一实例
 public static Singleton getInstance() {
 return instance;
 }
}

注意instance是被static修饰的
static表示静态的,这里修饰类,指的是“类属性”,instance即使类对象里面持有的属性。
SIngleton.class(从class文件加载到内存中表示这个类的一个数据结构,每个类只存在一个类对象)
因此instance指向的这个对象,就是唯一的一个对象!!

构造方法被private修饰,不能通过构造方法获取类实例

只能通过getinstance方法获取唯一实例

  • 优点:没有加锁,执行效率会提高。

  • 缺点:类加载时就初始化,浪费内存。

🎍懒汉模式

类加载的时候不创建实例. 第⼀次使⽤的时候才创建实例.

懒汉模式,顾名思义就是懒,没有对象需要调用它的时候不去实例化,有人来向它要对象的时候再实例化对象

🌸单线程版(线程不安全)

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

这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。
因为没有加锁synchronized,所以严格意义上它并不算单例模式

上面的懒汉模式的实现是线程不安全的.

  • 线程安全问题发生在首次创建实例时. 如果在多个线程中同时调用 getInstance 方法, 就可能导致
    创建出多个实例.

但是一旦实例已经创建好了, 后面再多线程环境调用 getInstance 就不再有线程安全问题了(不再修改instance 了)

🌸多线程版

所以我们对上述代码进行了优化

我们可以加上 synchronized 可以改善这里的线程安全问题.

优化代码如下:

class Singleton {
	private static Singleton instance = null;
	private Singleton() {}
	public synchronized static Singleton getInstance() {
		if (instance == null) {
			instance = new Singleton();
		}
		return instance;
	}
}
  • 优点:第一次调用才初始化,避免内存浪费。

  • 缺点:必须加锁 synchronized 才能保证单例,但加锁会影响效率

该版的懒汉模式还存在一个问题,就是内存可见性的问题,不知道内存可见性的宝子可以去看看博主写的volatile 关键字、wait 和 notify方法详解里面对volati关键字部分的讲解

🌸多线程版(改进)

所以我们针对上述多线程版的懒汉模式进行了改进

以下代码在加锁的基础上, 做出了进一步改动:

  • 使用双重 if 判定, 降低锁竞争的频率.

  • 给 instance 加上了 volatile.

代码实现如下:

class Singleton {
    private static volatile Singleton instance = null;
    private Singleton() {}
    public static Singleton getInstance() {
        //需要用到时进行创建
        if(instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

那么双重 if 判定 / volatile是怎么达到这样的效果的呢

  • 加锁 / 解锁是一件开销比较高的事情. 而懒汉模式的线程不安全只是发生在首次创建实例的时候.

  • 因此后续使用的时候, 不必再进行加锁了.

  • 外层的 if 就是判定下看当前是否已经把 instance 实例创建出来了.

  • 同时为了避免 “内存可见性” 导致读取的 instance 出现偏差, 于是补充上 volatile .

  • 当多线程首次调用 getInstance, 大家可能都发现 instance 为 null, 于是又继续往下执行来竞争锁,其中竞争成功的线程, 再完成创建实例的操作.

  • 当这个实例创建完了之后, 其他竞争到锁的线程就被里层 if 挡住了. 也就不会继续创建其他实例.

比如以下实例

  1. 有三个线程, 开始执⾏ getInstance , 通过外层的 if (instance == null) 知道了实例还没有创建的消息. 于是开始竞争同⼀把锁
    在这里插入图片描述

  2. 其中线程1 率先获取到锁, 此时线程1 通过⾥层的 if (instance == null) 进⼀步确认实例是否已经创建. 如果没创建, 就把这个实例创建出来.

  3. 当线程1 释放锁之后, 线程2 和 线程3 也拿到锁, 也通过⾥层的 if (instance == null) 来
    确认实例是否已经创建, 发现实例已经创建出来了, 就不再创建了
    在这里插入图片描述

  4. 后续的线程, 不必加锁, 直接就通过外层 if (instance == null) 就知道实例已经创建了,从⽽不再尝试获取锁了. 降低了开销.、
    在这里插入图片描述

⭕总结

关于《【多线程】多线程案例——单例模式详解(包含懒汉模式,饿汉模式)》就讲解到这儿,感谢大家的支持,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AtCoder ABC342 A-D题解

华为出的比赛&#xff1f; 好像是全站首个题解哎&#xff01; 比赛链接:ABC342 Problem A: 稍微有点含金量的签到题。 #include <bits/stdc.h> using namespace std; int main(){string S;cin>>S;for(int i0;i<s.size();i){if(count(S.begin(),S.end(),S[i…

多维时序 | Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测

多维时序 | Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测 目录 多维时序 | Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 Matlab实现基于VMD-DBO-GRU、VMD-GRU、GRU的多变量时间序列预测…

书生·浦语大模型实战营第六节课作业

基础作业 python run.py --datasets ceval_gen --hf-path /root/model/Shanghai_AI_Laboratory/internlm2-chat-7b/ --tokenizer-path /root/model/Shanghai_AI_Laboratory/internlm2-chat-7b/ --tokenizer-kwargs padding_sideleft truncationleft trust_remote_codeTrue --m…

嵌入式软件分层设计的思想分析

“嵌入式开发&#xff0c;点灯一路发” 那今天我们就以控制LED闪烁为例&#xff0c;来聊聊嵌入式软件分层: ——————————— | | | P1.1 |-----I<|--------------<| | | | P2.1 |-------------/ ---------…

Seata分布式事务实战AT模式

目录 分布式事务简介 典型的分布式事务应用场景 两阶段提交协议(2PC) 2PC存在的问题 什么是Seata&#xff1f; Seata的三大角色 Seata AT模式的设计思路 一阶段 二阶段 Seata快速开始 Seata Server&#xff08;TC&#xff09;环境搭建 db存储模式Nacos(注册&配…

HTB-Bizness

一、信息收集 访问ip自动跳转域名&#xff0c;host绑定域名后访问 目录爆破 有一个登录目录&#xff0c;访问发现是apahce ofbiz登录页面 发现存在漏洞 二、漏洞利用 在github上找到了图形化利用工具 使用工具反弹shell 得到flag 三、权限提升 从本地利用python开启http服务…

远程连接服务器及可视化方法(Win和Linux)

1.win端 1、通过SSH连接至服务器 在window下&#xff0c;打开命令行提示符&#xff08;快捷键winr后输入cmd回车&#xff09; 在命令行中输入 ssh 服务器上的用户名192.168.50.204回车并输入服务器上的用户登录密码 至此&#xff0c;已成功通过SSH连接至服务器。 2、通过…

复旦大学EMBA联合澎湃科技:共议科技迭代 创新破局

1月18日&#xff0c;由复旦大学管理学院、澎湃新闻、厦门市科学技术局联合主办&#xff0c;复旦大学EMBA项目、澎湃科技承办的“君子知道”复旦大学EMBA前沿论坛在厦门成功举办。此次论坛主题为“科技迭代 创新破局”&#xff0c;上海、厦门两地的政策研究专家、科学家、科创企…

集合的并发修改异常问题

使用迭代器遍历集合时&#xff0c;同时在删除集合中的数据&#xff0c;程序就会出现并发修改异常的错误。 import java.util.ArrayList; import java.util.Iterator; import java.util.List;public class _Exception {public static void main(String[] args) {List<String…

【GAD】DOMINANT个人解读/学习

SDM2019&#xff0c;这是一篇图异常检测领域的经典方法. 问题定义 在本文中&#xff0c;我们使用手写体来表示集合&#xff08;例如&#xff0c; V \mathcal{V} V&#xff09;&#xff0c;粗体小写字母&#xff08;例如&#xff0c; x \mathbf{x} x&#xff09;来表示向量&…

优化测试稳定性的失败重试工具:pytest-rerunfailures详解!

一.前言 笔者在执行自动化测试用例时&#xff0c;会发现有时候用例失败并非代码问题&#xff0c;而是由于服务正在发版&#xff0c;导致请求失败&#xff0c;从而降低了自动化用例的稳定性&#xff0c;最后还要花时间定位到底是自身case的原因还是业务逻辑问题&#xff0c;还是…

安全测试:史上最全的攻防渗透信息收集方法、工具!

信息收集的意义 信息收集对于渗透测试前期来说是非常重要的。正所谓&#xff0c;知己知彼百战不殆&#xff0c;信息收集是渗透测试成功的保障&#xff0c;只有我们掌握了目标网站或目标主机足够多的信息之后&#xff0c;才能更好地进行渗透测试。 信息收集的方式可以分为两种…

如何利用EXCEL批量插入图片

目录 1.excel打开目标表格&#xff1b; 2.点开视图-宏-录制宏&#xff0c;可以改宏的名字或者选择默认&#xff1b; 3.然后点开视图-宏-查看宏 4.点编辑进去 5.修改代码&#xff1a; &#xff08;1&#xff09;打开之后会显示有一堆代码 &#xff08;2&#xff09;将这个…

git之分支管理

一.理解分支 我们看下面这张图片&#xff1a; 在版本回退⾥&#xff0c;你已经知道&#xff0c;每次提交&#xff0c;Git都把它们串成⼀条时间线&#xff0c;这条时间线就可以理解为是⼀个分⽀。截⽌到⽬前&#xff0c;只有⼀条时间线&#xff0c;在Git⾥&#xff0c;这个分⽀…

SpringBootRest服务调用

目录 RestTemplate 依赖配置 自定义RestTemplate webCilent 依赖配置 自定义webCilent springboot中有两种方式实现Rest远程服务调用&#xff0c;分别是RestTemplate与webCilent。下面分别介绍一下这两种方式。 RestTemplate 依赖配置 RestTemplate是Spring Framework提供的…

使用GPT生成python图表

首先&#xff0c;生成一脚本&#xff0c;读取到所需的excel表格 import xlrddata xlrd.open_workbook(xxxx.xls) # 打开xls文件 table data.sheet_by_index(0) # 通过索引获取表格# 初始化奖项字典 awards_dict {"一等奖": 0,"二等奖": 0,"三等…

从Unity到Three.js(outline 模型描边功能)

指定模型高亮功能&#xff0c;附带设置背景颜色&#xff0c;获取随机数方法。 百度查看说是gltf格式的模型可以携带PBR材质信息&#xff0c;如果可以这样&#xff0c;那就完全可以在blender中配置好材质导出了&#xff0c;也就不需要像在unity中调整参数了。 import * as THRE…

牛客周赛 Round 34 解题报告 | 珂学家 | 构造思维 + 置换环

前言 整体评价 好绝望的牛客周赛&#xff0c;彻底暴露了CF菜菜的本质&#xff0c;F题没思路&#xff0c;G题用置换环骗了50%, 这大概是唯一的亮点了。 A. 小红的字符串生成 思路: 枚举 a,b两字符在相等情况下比较特殊 a, b input().split() if a b:print (2)print (a)pri…

【Redis】搞懂过期删除策略和内存淘汰策略

1、过期删除策略 1.1、介绍 Redis 是可以对 key 设置过期时间的&#xff0c;因此需要有相应的机制将已过期的键值对删除&#xff0c;而做这个工作的就是过期键值删除策略。 每当我们对一个 key 设置了过期时间时&#xff0c;Redis 会把该 key 带上过期时间存储到一个过期字典…

3.openEuler物理存储及逻辑卷管理(一):磁盘存储挂载与使用

openEuler OECA认证辅导,标红的文字为学习重点和考点。 如果需要做实验,建议下载麒麟信安、银河麒麟、统信等具有图形化的操作系统,其安装与openeuler基本一致。 磁盘大类: HDD, (Hard Disk Drive的缩写) : 由一个或者多个铝制或者玻璃制成的磁性碟 片,磁头,…