当前位置: 首页 > news >正文

【spring】从spring是如何避免并发下获取不完整的bean引发的思考 什么是双重检查锁 什么是java内存模型

本文将通过简述spring是如何避免并发下获取不完整的bean,延伸出双重检查锁、volatile、JMM的概念,将这些知识点都串联起来;
若发现错误,非常欢迎在评论区指出;csdn博主:孟秋与你

文章目录

  • 双重检查锁(Double-Checked Locking)
  • java内存模型(Java Main Memory)
    • 内存模型三大特性:原子性、可见性、顺序性
    • volatile
    • happen before原则
    • 完善单例模式+double checked
  • spring如何做到避免并发下获取不完整的bean

双重检查锁(Double-Checked Locking)

这是一个非常古老且经典的概念,在单例模式中会用到;我们可以看看这段经典的demo代码:

下面是创建单例模式的一段代码,在单线程中 不会有问题

public class Singleton {  // 加上 volatile 关键字private volatile static Singleton instance = null;  private Singleton(){}  public static Singleton  getInstance() {  // do something or checkif(instance == null) {  instance = new Singleton();  }  return instance;  }  
}  

但是在多线程,尤其高并发下,极容易引发问题, 我们可以设想一下:
thread1和thread2同时进入getInstance()方法,同时执行if(instance == null)校验,那么两个线程执行的判断都为true,都会创建实例;

如果将整个方法加上 synchronized 锁,问题解决了,但是锁整个方法 锁的粒度太大了一点,我们只需要保证创建实例的时候上锁即可

所以我们将代码稍微调整一下:

public class Singleton {  private static Singleton instance = null;  private Singleton(){}  public static Singleton  getInstance() {  if(instance == null) {  // do something or checksynchronized(Singleton.class){if(instance == null) {instance = new Singleton(); }}}  return instance;  }  
}  

上诉代码仍会有问题,因为初始化Singleton 和 将对象地址写到instance字段 的顺序是不确定的,所以可能出现在某个线程new Singleton()时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。

接下来,我们简单讲述一下java内存模型的三大特性。

java内存模型(Java Main Memory)

java内存模型 简称JMM,在并发编程的艺术这本书里面会提到,JMM是一个抽象的概念,通俗点说 内存模型是我们用于理解cpu的操作提出来的一个概念,便于我们理解,但其实细节不一定是这么做的。
(对java开发者来说 理解止步于此即可,不用咬文嚼字, 把这个抽象概念当成理论就行了;换句话说只有抬杠的时候才可能指出 JMM是个抽象概念)

内存模型三大特性:原子性、可见性、顺序性

原子性:经典的 i++ 操作就不是原子性的,volatile不能完全当成锁来用 非常大的一个原因就是volatile不保证原子性

volatile int a = 1;
// 伪代码
public void test(){fori{// 会有线程安全问题i++;}
}

可见性: 多个线程之间可以看到状态已经被修改,线程安全中,可见性是一个非常非常重要的概念

顺序性:通俗点理解,我们写的代码顺序 cpu却并不一定是这么执行的,它在(自认为)最终结果不变的前提下发生指令重排;单线程下,指令重排是一种优化,cpu以它自己觉得效率更高的指令顺序来执行;但是如果在多线程下,指令重排就可能会导致问题,我们上一节提到的 “初始化Singleton 和 将对象地址写到instance字段 的顺序是不确定的” ,正是发生了指令重排。

volatile

轻量锁,volatile不能保证原子性,但是可以保证可见性、顺序性,所以在简单的结构里面,可以当成锁来用。
(在博主其它博客有提到 主页搜volatile可以看到)

volatile保证顺序性是因为阻止到了指令重排,我们说过 指令重排是cpu以它自己的方式来排序的,但这种方式肯定是不能乱来的,需要遵循一定的原则,这个原则就称为:happen before

happen before原则

程序顺序规则:在一个线程内,按照代码的顺序,前面的操作会在后面的操作之前发生。监视器锁规则:对一个锁的解锁操作,happen-before任何后续对同一个锁的加锁操作。volatile变量规则:对一个volatile变量的写操作,happen-before任何后续对该变量的读操作。线程启动规则:对线程的start()方法的调用,happen-before该线程开始运行。线程终止规则:线程的join()方法的返回,happen-before该线程的所有操作都完成。其他:还有一些其他规则,例如对同一个对象的操作顺序、各个线程之间的操作等。

完善单例模式+double checked

在有了以上概念后,回到单例模式那个例子,我们怎么解决
“初始化Singleton 和 将对象地址写到instance字段 的顺序是不确定的” 这个问题呢?

加上 volatile 关键字即可,代码如下:

public class Singleton {  private volatile static Singleton instance = null;  private Singleton(){}  public static Singleton  getInstance() {  if(instance == null) {  // do something or checksynchronized(Singleton.class){if(instance == null) {instance = new Singleton(); }}}  return instance;  }  
}  

spring如何做到避免并发下获取不完整的bean

tips: 基础概念: bean在实例化的时候 会放入三级缓存,然后进行一些属性注入 ,放入二级缓存,最后bean彻底创建后,放入一级缓存;放入一级缓存后 会将二三级缓存清除。
getBean发现为null时,会doCreateBean

我们可以想象下述场景:

线程1创建bean: 放入三级二级缓存,最后创建完成放入一级缓存

线程2会先读取一级缓存 此时线程1还没有存放至一级缓存 所以为空,等到读取二级三级缓存时 仍然可能出现为空的情况
(比如此时线程1存入了一级缓存 存放一级缓存会将二级、三级缓存清空)
如果为空,将会再次从一级缓存进行读取(double check)

博主比较喜欢用自己的理解和语言来描述,下面是画的一个简图 ,简陋但应该能看懂,网上也有专业的图 各位可以自行搜索。
在这里插入图片描述

注意:以上结论并不是严谨的结论,实际spring处理bean非常非常的复杂!

源码位于DefaultSingletonBeanRegistry 类的getSingleton方法

启动项目打断点会发现先执行的以下方法:
从代码来看

    @Nullablepublic Object getSingleton(String beanName) {return this.getSingleton(beanName, true);}@Nullableprotected Object getSingleton(String beanName, boolean allowEarlyReference) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null && this.isSingletonCurrentlyInCreation(beanName)) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null && allowEarlyReference) {synchronized(this.singletonObjects) {singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {singletonObject = this.earlySingletonObjects.get(beanName);if (singletonObject == null) {ObjectFactory<?> singletonFactory = (ObjectFactory)this.singletonFactories.get(beanName);if (singletonFactory != null) {singletonObject = singletonFactory.getObject();this.earlySingletonObjects.put(beanName, singletonObject);this.singletonFactories.remove(beanName);}}}}}}return singletonObject;}
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {Assert.notNull(beanName, "Bean name must not be null");synchronized(this.singletonObjects) {Object singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {if (this.singletonsCurrentlyInDestruction) {throw new BeanCreationNotAllowedException(beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)");}if (this.logger.isDebugEnabled()) {this.logger.debug("Creating shared instance of singleton bean '" + beanName + "'");}this.beforeSingletonCreation(beanName);boolean newSingleton = false;boolean recordSuppressedExceptions = this.suppressedExceptions == null;if (recordSuppressedExceptions) {this.suppressedExceptions = new LinkedHashSet();}try {singletonObject = singletonFactory.getObject();newSingleton = true;} catch (IllegalStateException var16) {IllegalStateException ex = var16;singletonObject = this.singletonObjects.get(beanName);if (singletonObject == null) {throw ex;}} catch (BeanCreationException var17) {BeanCreationException ex = var17;if (recordSuppressedExceptions) {Iterator var8 = this.suppressedExceptions.iterator();while(var8.hasNext()) {Exception suppressedException = (Exception)var8.next();ex.addRelatedCause(suppressedException);}}throw ex;} finally {if (recordSuppressedExceptions) {this.suppressedExceptions = null;}this.afterSingletonCreation(beanName);}if (newSingleton) {this.addSingleton(beanName, singletonObject);}}return singletonObject;}}

我们自己调试的时候就会发现,不同的bean情况不一样,处理非常非常复杂!

关于这个问题,博主推荐的答案为:

在源码getSingleton方法中,用到了双重检查锁,
先从一级缓存查找 ,找不到会去二级或三级查找, 
如果再找不到 会回到一级缓存查找

相关文章:

【spring】从spring是如何避免并发下获取不完整的bean引发的思考 什么是双重检查锁 什么是java内存模型

本文将通过简述spring是如何避免并发下获取不完整的bean&#xff0c;延伸出双重检查锁、volatile、JMM的概念&#xff0c;将这些知识点都串联起来&#xff1b; 若发现错误&#xff0c;非常欢迎在评论区指出&#xff1b;csdn博主&#xff1a;孟秋与你 文章目录 双重检查锁(Doubl…...

【计算机网络一】网络学习前置知识

目录 网络中必备概念 1.什么是局域网与广域网&#xff1f; 2.什么是IP地址 3.什么是端口号 4.什么是协议 5.OSI七层模型 6.TCP/IP四层模型 网络中必备概念 本篇文章旨在分享一些计算机网络中的常见概念&#xff0c;对于初学者或者准备学习计算机网络的人会有帮助。 1.什么…...

nuScenes数据集使用的相机的外参和内参

因为需要用不同数据集测试对比效果&#xff0c;而一般的模型代码里实现的检测结果可视化都是使用open3d的Visualizer在点云上画的3d框&#xff0c;展示出来的可视化效果很差&#xff0c;可能是偷懒&#xff0c;没有实现将检测结果投影到各相机的图像上&#xff0c;所以检测效果…...

数据结构与算法:贪心算法与应用场景

目录 11.1 贪心算法的原理 11.2 经典贪心问题 11.3 贪心算法在图中的应用 11.4 贪心算法的优化与扩展 总结 数据结构与算法&#xff1a;贪心算法与应用场景 贪心算法是一种通过选择当前最佳解来构造整体最优解的算法策略。贪心算法在很多实际问题中都取得了良好的效果&am…...

音频编解码器音频文件格式

0 Preface/Foreword 1 音频编解码器 算法压缩越高&#xff0c;那么音频延迟越大&#xff0c;音频效果越好。 1.1 SBC SBC: sub-band coding&#xff0c;自带编码 A2DP强制规定使用的audio编解码器。 在音视频中&#xff0c;为了增加用户体验&#xff0c;规避视频和音频的不…...

FreeSWITCH JSON API

仅举几例&#xff1a; fs_cli -x json {"command" : "status", "data" : ""} fs_cli -x json {"command" : "sofia.status", "data" : ""} fs_cli -x json {"command" : "…...

学习docker第三弹------Docker镜像以及推送拉取镜像到阿里云公有仓库和私有仓库

docker目录 1 Docker镜像dockers镜像的进一步理解 2 Docker镜像commit操作实例案例内容是ubuntu安装vim 3 将本地镜像推送至阿里云4 将阿里云镜像下载到本地仓库5 后记 1 Docker镜像 镜像&#xff0c;是docker的三件套之一&#xff08;镜像、容器、仓库&#xff09;&#xff0…...

一文掌握Kubernates核心组件,构建智能容器管理集群

1.Kubernates简要概述 Kubernates&#xff08;常称为K8s&#xff0c;因省略了“ubernate”中的8个字符&#xff09;是Google开源的容器编排平台&#xff0c;专为简化和自动化应用服务的部署、扩展和管理而设计。它将应用与底层的服务器抽象开来&#xff0c;提供了自动化的机制…...

正则表达式快速入门

正则表达式是由一系列元字符&#xff08;Meta-characters&#xff09;组成的模式&#xff0c;用于定义搜索或替换文本的规则。元字符具有特殊含义&#xff0c;用于指定搜索模式的结构。以下是一些常用的正则表达式元字符及其功能&#xff1a; 字符匹配符 符号含义.匹配除 \r\…...

【小程序】-基础语法(二)

文章目录 知识回顾前言微信小程序开发一、模板语法2.1 数据绑定2.2 条件渲染2.3 列表渲染三、内置API3.1 网络请求3.2 界面交互3.3 本地存储3.4 API 特征3.5 相册/拍照3.6 小练习四、事件处理4.1 事件对象4.2 组件事件五、生命周期5.1 页面生命周期5.2 应用生命周期知识回顾 前…...

js 填充数组

let arr Array.from({ length: 10 }, (_, index) > index)console.log(arr) 人工智能学习网站 https://chat.xutongbao.top...

AI创作3款软件分享,助力内容创作者高效产出优质作品

为了增加创造力和作品质量&#xff0c;许多创作者开始利用人工智能辅助工具。这些工具不仅可以帮助我们迅速生成各种类型的内容&#xff0c;例如文章、绘画、视频广告等&#xff0c;还提供语法检查和优化建议等实用功能。本文将向大家推荐三款适用于Ai先行者、Tracup、Adoe Fir…...

A survey of loss functions for semantic segmentation——论文笔记

摘要 图像分割一直是一个活跃的研究领域&#xff0c;因为它有着广泛的应用范围&#xff0c;从自动疾病检测到自动驾驶汽车。过去五年中&#xff0c;各种论文提出了用于不同场景&#xff08;如数据偏斜、稀疏分割等&#xff09;的目标损失函数。在本文中&#xff0c;我们总结了…...

docker部署es与kibana Mac

1. 创建网络 神一样的链接&#xff0c;不用谢&#xff1a; 1.Docker命令链接&#xff1a;黑马整理的docker速成链接 2.jdk11链接&#xff1a;jdk11 3.神资源链接&#xff1a;别点&#xff0c;要脸 注意&#xff1a;es需要先安装jdk环境&#xff0c;推荐jdk11&#xff0c;否则…...

redis的渐进式哈希?说一下细节?------面试题分享

渐进式哈希&#xff08;Progressive Hashing&#xff09;是 Redis 中的一种优化机制&#xff0c;用于在执行 HGETALL 命令时逐步读取哈希表中的所有字段。这种机制避免了一次性加载大量数据到内存&#xff0c;从而减少了内存消耗和提高系统的响应速度。 渐进式哈希的背景 在 R…...

javaWeb项目-springboot+vue-车辆管理系统功能介绍

本项目源码&#xff08;点击下方链接下载&#xff09;&#xff1a;java-springbootvue车辆管理系统源码(项目源码-说明文档)资源-CSDN文库 项目关键技术 开发工具&#xff1a;IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架&#xff1a;ssm、Springboot 前端&#xff1…...

redis和memcached的区别

Redis和Memcached都是流行的内存缓存数据库&#xff0c;但它们有一些区别&#xff1a; 数据类型&#xff1a;Redis支持更多的数据类型&#xff0c;包括字符串、哈希、列表、集合和有序集合等&#xff0c;而Memcached只支持简单的键值对。 持久化&#xff1a;Redis支持数据的持…...

构建安全基石:网络安全等级保护定级指南

在数字化时代&#xff0c;网络安全已成为企业与个人不可忽视的重要课题。网络安全等级保护定级指南&#xff0c;作为国家指导网络安全保护的重要文件&#xff0c;为各类机构提供了精准的安全防护蓝图。本文旨在深度解析网络安全等级保护定级指南的精髓&#xff0c;助力建构全面…...

PyQt 入门教程(3)基础知识 | 3.1、使用QtDesigner创建.ui文件

文章目录 一、使用QtDesigner创建.ui文件1、创建.ui文件2、生成.py文件3、使用新生成的.py文件4、编辑新生成的.py文件 一、使用QtDesigner创建.ui文件 1、创建.ui文件 打开PyCharm&#xff0c;使用自定义外部工具QtDesigner创建mydialog.ui文件&#xff0c;如下&#xff1a; …...

解锁金融大门,你的基从备考秘籍全揭秘!

大家好&#xff01;随着金融行业的快速发展&#xff0c;基金从业资格证已经成为越来越多金融从业者的必备证书。为了帮助大家更好地备考&#xff0c;今天我们就来聊聊基金从业资格证&#xff01; 一、考试时间 2024年下半年基金从业资格考试时间为11月9日。准考证打印的时间是…...

线程与协程

1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指&#xff1a;像函数调用/返回一样轻量地完成任务切换。 举例说明&#xff1a; 当你在程序中写一个函数调用&#xff1a; funcA() 然后 funcA 执行完后返回&…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢

随着互联网技术的飞速发展&#xff0c;消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁&#xff0c;不仅优化了客户体验&#xff0c;还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用&#xff0c;并…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

docker 部署发现spring.profiles.active 问题

报错&#xff1a; org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...

Reasoning over Uncertain Text by Generative Large Language Models

https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

CSS设置元素的宽度根据其内容自动调整

width: fit-content 是 CSS 中的一个属性值&#xff0c;用于设置元素的宽度根据其内容自动调整&#xff0c;确保宽度刚好容纳内容而不会超出。 效果对比 默认情况&#xff08;width: auto&#xff09;&#xff1a; 块级元素&#xff08;如 <div>&#xff09;会占满父容器…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器

拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件&#xff1a; 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...