DCL单例模式是如何保证数据安全的?

news/2024/5/21 1:15:51/文章来源:https://blog.csdn.net/qq_16485855/article/details/129181388

承接上文证明CPU指令是乱序执行的

DCL单例(Double Check Lock)到底需不需要volatile?

new对象这一步,对应着汇编层面的这3个指令,

指令0是申请空间,设置默认值;

指令7是执行构造方法,设置初始值;

指令4是建立栈中的对象名称和堆中对象的关联。

下面详细的介绍下该过程,

在多线程访问的情况下:线程1执行指令1,new对象,此时m值为0即还没有调用构造方法的时候t已经有默认值0了。

在单线程的情况下,只要不影响最终一致性,4和7两个指令可以任意换顺序。

在多线程的情况下,就会出现问题:

假设4和7突然换了顺序即发生了指令重排(new一半发生了指令重排),t和astore先建立关联,

t就指向了创建一半的对象;

当t指向了new了一半的对象的时候,在这个时间点,第二个线程过来了,第二个线程首先判断是否为空,

此时这个t不等于空了,因为它已经指向了初始化一半的对象了,不为空的话,就不需要上锁了,直接拿来用就可以了,此时就访问到了m为0这个状态的对象了即使用了初始化一半的对象。

这个问题就是由于指令的重排序造成的,此时你可能有这个疑问?

都上锁了,只有一个线程才能运行,其他线程居然还能访问到中间状态?

synchroinzed确实可以保证原子性,也能够保证可见性,但是唯一不能保证的是有序性。

synchroinzed里面的顺序,可以随便换,只要能保证单线程的最终一致性。

如果两段代码都是位于同一把锁的情况下,绝对不可能这个线程执行了一半,另外的线程可以看到,

判断实例是否为空的的代码是未上锁的,第二个线程来的时候,不需要上锁就可以进行这个判断。

上锁的代码和不上锁的代码之间是并发运行的,它们之间没有互斥和序列化,未上锁的代码就可以访问到中间状态,虽然上了锁的代码new对象实例化了一半,没有上锁的代码也是可以访问到的,这个时候如果重排序的话,就会访问到中间状态。

这2个指令(指令4和7)为什么可以换顺序?难道java中的指令都可以随便换顺序吗?

不能随便换顺序,比如一个对象还没有new完的时候,就不可能执行finalize方法进行回收或者还没有new完就不能启动。

程序里面规定了这8种情形不可以换顺序,这种被称为happens-before原则,

对象的创建过程

在run main方法的时候,先将源码编译成字节码,然后通过Idea的Show Bytecode With Jclasslib插件工具查看字节码的内容,

这是main方法中生成的整个字节码,相当于java的汇编语言,这段字节码一共有5条语句构成。

new对象的时候转换成java的二进制码会执行这5条指令:

第一步new(对应指令0),申请一块空间,刚刚申请出这块空间的时候m是0;

指令4是一个特殊调用,T.init方法是指执行构造方法,构造方法执行完之后,m才会变成8;

astore指令将栈里面的t和这个对象建立关联。

new一个对象的时候,会有3步构成:

  • 指令0:申请一块空间,初始值是0

  • 指令4:执行构造方法,初始化成8

  • 指令7:将栈中的t和对象关联起来

java中为什么要给个初始值0?

在c++或c语言中申请一块内存的时候,里面有一个成员变量,这个成员变量最开始的值会是多少?

如果不给它赋予初始值,最开始的值叫遗留值,是指原来在这块内存中被用过的值,具体值是随机的,这是c或c++的做法,这也是c++不安全的地方。

java的做法是先给它一个默认的初始化值0,java为什么这么做?

主要是为了安全,如果这里装的正好是上一个程序的密码,如果随随便便的就能访问到上一个程序的遗留值就能够拿到密码了,java做了很多安全上的设计。

著名的this溢出问题

 

this实际上是方法里面一个默认的形参,this指向的就是自己这个对象的本身。

当new这个对象的时候,申请空间里面的num值会有一个变成0的过程,调用完成构造方法之后才会变成8,在这个构造方法还没有完成的时候,这个线程就启动了,这里的this.num会把0打印出来;

这两条语句(指令4和7)是可以换顺序的;

t看成this,当m=0的时候,4和7交换位置,先执行astore(astore执行完了才会给this.num赋值),构造方法还没有执行完呢,就可以直接建立关联(栈中的t和对象建立关联)了,这个this就是初始化了一半的对象, 这个就叫溢出问题,形象的比喻:穿衣服还没有穿好,就出去见人了。

所以不要在构造方法中启动线程,因为很可能访问到对象初始化一半的状态,不是你期望的那个结果。

单例模式

构造方法被private修饰,目的就是不给别人用,只能自己创建当前类的对象。

但这种写法有问题,在这个对象还没有用的时候,直接先把它创建出来了,会造成空间和cpu资源的浪费,能不能等用到的时候再new?

(为了暴露多线程的问题这里设了一个毫秒)

这是懒汉式单例模式即什么时候用到了才去new,但会出现更大的问题:在多线程访问的情况下,很可能访问的不是同一个对象。

将方法上锁,在同一个时间段内只能有一个线程执行里面的代码,这个线程执行完了,另外一个线程才允许执行里面的代码,这是多线程的原子性,整个的语句当作一个原子,不可分割。

如果锁定的代码中有一些业务逻辑,就会发现锁定的代码耗时就会太长了,比如从db中读取数据的业务逻辑没有必要上锁,为了让锁的粒度稍微变得细一点,让可执行的时间效率稍微高一点,则不给整个方法上锁,

这样缩小了锁的粒度,不将业务代码的是否等于空的判断加锁,等判断为空之后,再上锁,上完锁,再new对象,这是追求效率的写法,但在多线程访问的情况下能不能保证数据的一致性?

第一个线程判断对象为空,在获取锁之前,切换给了第二个线程执行,第二个线程判断对象为空,获取锁,上完锁后,new对象,把锁释放,然后第二个线程结束,在这个时候第一个线程继续运行,因为第一个线程已经判断完了,直接申请上锁,此时是可以上锁成功的,因为第二个线程已经把锁释放了,线程1又new了一个对象。

上完锁之后,再判断是否依然为空,如果依然为空,说明在上锁的过程当中没有其他人把它给new出来,如果还依然保持为空,就把它new出来。

第一个线程在做第一步判断的时候是不为空的,因为第二个线程已经把它new出来了,所以不会再new第二遍,这样就会保证单例的唯一。

双重检查中间加了一个锁,这是很多开源软件非常标准的做法。

最外层的判空有必要吗?

一定有必要存在,假如不做这个判断,确实可以保证整个单例只有一个,但是效率会偏低。

如果有1万个线程都来去new这个对象 ,如果外面没有这层检查,1万个线程都会申请上锁,因为所有线程来了之后都会执行这句话,申请上锁的过程是一个非常重要的过程,只要有外面这层检查的话,只有一个线程new成功了,其他线程来了判断一下不为空,就再也不需要上锁的过程了,这个判断仅1、2个纳秒而已,而这个上锁的动作需要好几百个纳秒,所以外面那层检查是不可以省略的。

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

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络概述 第二部分

5.网络分层 ①OSI 7层模型 数据链路层 (Data Link Layer) 实现相邻(Neighboring)网络实体间的数据传输 成帧(Framing):从物理层的比特流中提取出完整的帧 错误检测与纠正:为提供可靠数据通信提供可能 …

stm32f407探索者开发板(二十一)——窗口看门狗

文章目录一、窗口看门狗概述1.1 看门狗框图1.2 窗口看门狗工作过程总结1.3 超时时间1.4 为什么需要窗口看门狗1.5 其他注意事项二、常用寄存器和库函数2.1 控制寄存器WWDG_ CR2.2 配置寄存器WWDG_ CFR2.3 状态寄存器WWDG_SR三、手写窗口看门狗3.1 配置过程3.2 初始化窗口看门狗…

【微信小程序】-- 常用视图容器类组件介绍(六)

💌 所属专栏:【微信小程序开发教程】 😀 作  者:我是夜阑的狗🐶 🚀 个人简介:一个正在努力学技术的CV工程师,专注基础和实战分享 ,欢迎咨询! &#…

LeetCode 725. 分隔链表

LeetCode 725. 分隔链表 难度:middle\color{orange}{middle}middle 题目描述 给你一个头结点为 headheadhead 的单链表和一个整数 kkk ,请你设计一个算法将链表分隔为 kkk 个连续的部分。 每部分的长度应该尽可能的相等:任意两部分的长度差…

绿通科技在创业板开启申购:超额募资约19亿元,收入依赖贴牌

2月23日,广东绿通新能源电动车科技股份有限公司(下称“绿通科技”,SZ:301322)开启申购。据贝多财经了解,绿通科技本次上市的发行价为131.11元/股,发行数量为1749万股,市盈率73.75倍。 按发行价…

逆向 x品会 edata

逆向 x品会 edata 版本 7.88.6 帖子底部有参考说明 charles 抓包 目标字段 edata edata 搜索关键字 跟进找到是edata >>> KeyInfo native esNav 方法 private static native String esNav(Context context, String str, String str2, String str3, int i); …

XX项目自动化测试方案模板,你学会了吗?

目录 1、引言 2、自动化实施目标 3、自动化技术选型 4、测试环境需求 5、人员进度安排 总结感谢每一个认真阅读我文章的人!!! 重点:配套学习资料和视频教学 1、引言 文档版本 版本 作者 审批 备注 V1.0 Vincent XXX …

不会前端没事,用GWT Boot和Spring Boot构建Web程序

本文介绍了一种使用Java构建Web应用程序的方式,其中GWT或者J2CL是必不可少的,另外还有多个UI框架可以配套使用,比如Domino UI、VueGWT、GWT Material Design (GMD),React4J、WebFX,还有一些活跃低的框架GWTBootstrap3、…

【解决报错】‘jupyter‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件

在当前路径下使用cmd打开后,输入jupyter notebook出现如下错误: 通常可能出现的问题有两种: (1)你本身就没安装jupyter,如果你配置了anaconda,就自带jupyter,直接跳到问题2。如果确…

Apache Commons FileUpload Apache Tomcat拒绝服务漏洞解决方案

近日,安全狗应急响应中心关注到Apache官方发布安全公告,披露在Apache Commons FileUpload<1.5版本中存在一处拒绝服务漏洞(CVE-2023-24998)。Commons FileUpload是Apache组织提供的免费的上传组件。由于Apache Commons…

面向对象的一点小想法

接口里的方法可以写也可以不写 如果写的话,那么得是默认方法,需要在前面加个default 对于默认方法,能够重写,或者直接继承(也就是直接用) 比如下面: 就直接调用了接口的默认函数nibuhao&#…

R统计绘图-NMDS、环境因子拟合(线性和非线性)、多元统计(adonis2和ANOSIM)及绘图(双因素自定义图例)

这个推文也在电脑里待了快一年了,拖延症患者,今天终于把它发出来了。NMDS分析过程已经R统计-PCA/PCoA/db-RDA/NMDS/CA/CCA/DCA等排序分析教程中写过了。最近又重新看了《Numerical Ecology with R》一书,巩固一下知识,正好重新整理了一下发出…

Nacos源码启动

一、下载源码 为保证速度,国内推荐使用gitee:https://gitee.com/mirrors/Nacos.git 二、导入IDE中 参考之前文章配置国内Maven私服,快速更新工程。 三、启动过程,各种问题 找到启动入口: 先直接启动测试下&#xff…

oscp渗透测试认证该从哪里学起

当我决定要考OSCP时就马上打开浏览器,试图一下弄清楚课程内容和通过考试的方法,我不断的将指南和资源添加到书签中。渐渐的书签里存了许多资料,以至于我不知道从哪里开始学,学习命令吗?我学习编码吗?我使用…

北京/东莞/广州/深圳2023年上半年软考(中/高级)报名>>>

软考是全国计算机技术与软件专业技术资格(水平)考试(简称软考)项目,是由国家人力资源和社会保障部、工业和信息化部共同组织的国家级考试,既属于国家职业资格考试,又是职称资格考试。 系统集成…

扩展学习之时间戳趣谈

目录 一、介绍 二、转换工具 三、获取Unix时间戳的指令 四、普通时间转Unix时间戳 五、扩展 一、介绍 时间戳:一份数据在特定时间点存在的可验证的数据。 Unix时间戳(英文为Unix epoch, Unix time, POSIX time 或 Unix timestamp)&…

valgrind 移植到arm64 平台上总结

valgrind 介绍valgrind是查找内存泄漏的神器,你可以自动的检测许多内存管理和线程的bug,避免花费太多的时间在bug寻找上,使得你的程序更加稳固。 下载地址:https://valgrind.org/downloads/ 本人下载的是valgrind-3.19.0valgrind编…

.Net与程序集

一个简单的C#程序回想一下我们第一个.net 程序 hello world,它具有那些步骤呢?打开visual studio创建一个C# console的项目build运行程序这时候就有一个命令行窗口弹出来,上面打印着hello world。我们打开文件夹的bin目录,会发现里…

百度前端一面高频react面试题指南

React 高阶组件、Render props、hooks 有什么区别,为什么要不断迭代 这三者是目前react解决代码复用的主要方式: 高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一…

(考研湖科大教书匠计算机网络)第六章应用层-第七节:万维网WWW

获取pdf:密码7281专栏目录首页:【专栏必读】考研湖科大教书匠计算机网络笔记导航 文章目录一:万维网概述二:万维网应用(1)URI和URLA:URI和URL关系B:URL格式(2&#xff09…