DoubleCheck验证(双重检查锁和延迟初始化)

2023-10-30

场景

二话不说,直接上代码

public class DoubleCheckDemo {
	private  static  DoubleCheckDemo demo;
	public DoubleCheckDemo getDemo(){
		if(demo == null){
			synchronized (DoubleCheckDemo.class){
				if(demo == null){
					demo = new DoubleCheckDemo();
				}
			}
		}
		return demo;
	}
}

在实际场景我们有时候需要推迟一些开销比较高的对象内存化操作。我们想出了一种看似很“”聪明“”的操作。就像我代码写的,第一次检查实例不为空,就一顿操作加锁和初始化。所以节省直接大规模synchronized 代码的开销。
1.多个线程试图一起上创建对象,只有一个能成。
2.好了之后,以后再有线程来,我们一个判断直接挡住synchronized 的后续操作。
是不是感觉很完美,
但是这是错误的!!!
但是这是错误的!!!
但是这是错误的!!!
向来,重要的话说三遍!
因为如果代码执行到

	if(demo == null){

我们的demo可能还没有初始化完成!

寻根问底

其实我们这个操作。

	demo = new DoubleCheckDemo()

看似很简单,但是,暗藏了3个操作:
1.首先,要创建一个对象,得分配内存空间吧。
2.有了内存空间,我们得实例化这个对象吧。
3.设置demo指向内存地址
以上其实是废话,但是必须要讲。

引出问题

再放一段代码:

{
 a = 1;// 1
 b= 2;  // 2
 c = a + b; // 3
}

在一个线程中,我们都认为,1肯定happens - before 2 ,2 happens - before 3,的确JMM为我们程序员保证了这个玩意。
但是,JMM也给编译器和执行器做了一个保证,如果单线程下的
执 行 结 果!!!
执 行 结 果!!!
执 行 结 果!!!
不会发生改变,你们想怎么重排序都行,于是乎,编译器觉得,应该213比较好,也不会改变结果,所以执行顺序变成了213.

回到问题

通过上面的代码合解析,同理,
1.首先,要创建一个对象,得分配内存空间吧。
2.有了内存空间,我们得初始化这个对象吧。
3.设置demo指向内存地址
这三个操作只要执行结果不变,其他的怎么排都没事,所以有好事者,排序排成了132,这个不影响结果吧。
A线程:1 3 2
B线程:在设置demo指向地址之后直接来判断了,此时demo == null返回是false的!!!

那我们应该怎么办?
1.不给23重排序
2.给排,但是不给B看到,B来的时候我排完了。类似于宋襄公半渡江而不击。我们渡河渡了一半,你很仁慈的等我们渡完了,军队排列完整再和我们开战。

方案一:不给排

给对象声明volatile声明。这个声明23之间多线程中的重排序将会被禁止。
volatile关键字尚未成文。

方案二:排!

在jvm的类的初始化阶段(Class已经被加载,但是还没被线程使用之前),会执行类的初始化。执行期间,会获得一把锁,锁住。
所以可以这么干:

public class DoubleCheckDemo {
	public DoubleCheckDemo getDemo(){
		return DemoSync.demo;
	}
	
	private static class  DemoSync{
		public static  DoubleCheckDemo demo= new DoubleCheckDemo();
	}
}

A线程访问的时候,得到了这把锁,线程A执行初始化,这时候B来了,但是我这里是内部类在初始化,我获得的是这个初始化类的锁,你B能看到我重排序吗?你B再怎么也要先获得我这个类初始化的锁吧,但是A在持有。B只能眼睁睁看着。等A一顿操作完了,值也上了,之前
1.首先,要创建一个对象,得分配内存空间吧。
2.有了内存空间,我们得初始化这个对象吧。
3.设置demo指向内存地址
也做完了,B才拿到,这就没有任何问题了。

总结

直接双重检查锁和延迟初始化是不行的!!!
直接双重检查锁和延迟初始化是不行的!!!
直接双重检查锁和延迟初始化是不行的!!!
至少是不严谨的。所以我们可以通过两种方式来避免一切引起问题的可能性,把问题扼杀在摇篮中!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

DoubleCheck验证(双重检查锁和延迟初始化) 的相关文章

  • 方法返回类型前的 是什么意思?

    下面的方法返回一个List组成T类型元素 public
  • 在Maven中生成Version.java文件

    我有一个使用 Ant 脚本构建的 Java 项目 我正在尝试将项目转换为 Maven 其中一项任务生成一个名为 Version java 的 Java 源文件 其中包含编译时间戳的静态字符串表示形式 如下所示 package com foo
  • 通过Zuul上传大文件

    我在通过 zuul 上传大文件时遇到问题 我正在使用 apache commons 文件上传 https commons apache org proper commons fileupload https commons apache o
  • 自定义列表字段点击事件

    我正在编写一个应用程序 其中我创建了用于显示列表视图的自定义列表字段 我的 CustomListField 包含连续的一个图像和文本 我正在通过单击列表字段行获取字段更改侦听器 但我也想将字段更改侦听器放在图像上 谁能告诉我我该怎么做 这是
  • 为什么用scala写的代码比用java写的慢6倍?

    我不确定我在编写 scala 代码时是否犯了一些错误 问题是 The four adjacent digits in the 1000 digit number that have the greatest product are 9 9
  • 未装饰窗户的 Windows Snap 功能?

    有谁知道如何允许未装饰的窗户使用此功能 唯一的选择就是重新实施它 有任何想法吗 谢谢 可停靠可能是唯一的JToolBar http docs oracle com javase tutorial uiswing components too
  • 在光标所在行强制关闭!

    嘿 我正在尝试创建一个应用程序来查找存储在 SQlite 数据库中的 GPS 数据 但我面临一个问题 我构建了一个 DbAdapter 类来创建数据库 现在我尝试使用以下函数从另一个类获取所有数据上的光标 public Cursor fet
  • Mockito 和 Hamcrest:如何验证 Collection 参数的调用?

    我遇到了 Mockito 和 Hamcrest 的泛型问题 请假设以下界面 public interface Service void perform Collection
  • 2^31 次方的 Java 指数错误 [重复]

    这个问题在这里已经有答案了 我正在编写一个java程序来输出2的指数幂 顺便说一句 我不能使用Math pow 但是在 2 31 和 2 32 处我得到了其他东西 另外 我不打算接受负整数 My code class PrintPowers
  • 在java程序中使用c++ Dll

    我正在尝试使用System LoadLibrary 使用我用 C 编写的一个简单的 dll UseDllInJava java import com sun jna Library import com sun jna Native imp
  • IntelliJ Idea:将简单的 Java servlet(无 JSP)部署到 Tomcat 7

    我尝试按照教程进行操作here http wiki jetbrains net intellij Creating a simple Web application and deploying it to Tomcat部署 servlet
  • Joshua Bloch 的构建器设计模式有何改进?

    早在 2007 年 我就读过一篇关于 Joshua Blochs 所采用的 构建器模式 的文章 以及如何修改它以改善构造函数和 setter 的过度使用 特别是当对象具有大量属性 其中大部分属性是可选的 时 本文对此设计模式进行了简要总结
  • 从三点求圆心的算法是什么?

    我在圆的圆周上有三个点 pt A A x A y pt B B x B y pt C C x C y 如何计算圆心 在Processing Java 中实现它 我找到了答案并实施了一个可行的解决方案 pt circleCenter pt A
  • 如何向页面添加 HTML 页眉和页脚?

    如何使用 itext 从 html 源添加标题到 pdf 目前 我们已经扩展了 PdfPageEventHelper 并重写了这些方法 工作正常 但当我到达 2 个以上页面时 它会抛出 RuntimeWorkerException Over
  • 如何使用 Jersey 将嵌套列表封送为 JSON?我得到一个空数组或一个包含数组的单元素字典数组

    我正在开发一个使用 Jersey 将对象转换为 JSON 的项目 我希望能够写出嵌套列表 如下所示 data one two three a b c 我想要转换的对象首先将数据表示为 gt gt 我认为 Jersey 会做正确的事情 以上输
  • 我们如何使用 thymeleaf 绑定对象列表的列表

    我有一个表单 用户可以在其中添加任意数量的内容表对象这也可以包含他想要的列对象 就像在 SQL 中构建表一样 我尝试了下面的代码 但没有任何效果 并且当我尝试绑定两个列表时 表单不再出现 控制器 ModelAttribute page pu
  • 即使禁用安全性,OAuth 令牌 API 也无法在 Elastic Search 中工作

    我是 Elastic search 新手 使用 Elastic search 版本 7 7 1 我想通过以下方式生成 OAuth 令牌弹性搜索文档 https www elastic co guide en elasticsearch re
  • Spring Data Rest 多对多 POST

    首先 让我解释一下我的用例 这非常简单 有一个用户实体和一个服务实体 我使用 UserService 作为连接实体 连接表 在用户和服务之间建立多对多关联最初 会有一些用户集和一些服务集 用户可以在任何时间点订阅任何服务 在这种情况下 将向
  • Java 中清除嵌套 Map 的好方法

    public class MyCache AbstractMap
  • GAE 无法部署到 App Engine

    我正在尝试从 Eclipse 发布 Web 应用程序 我在 GAE 上创建了四个项目 可以通过登录我的帐户并查看控制台来查看它们 我已经改变了appengine web xml到项目的应用程序 ID 如果我将其更改为 GAE 上第一个创建的

随机推荐

  • 基于STM32的ESP8266使用教程(四)(短篇)

    写在前面 在上一篇中介绍了通过单片机发送指令控制WIFI模块 本来想写一下时钟 I O口及串口的初始化函数 但是因为项目年代久远 一年前做的 怕有错误 误导博友 故不给出代码 见谅 本章要介绍Android端通过WIFI与手机建立连接并通信
  • 代码示例:面向对象——封装、继承、多态(多态的四种类型)

    面向对象 封装 继承 多态 多态的四种类型 1 封装 把客观事物封装成抽象的类 并且类可以把自己的数据和方法只让可信的类或者对象操作 对不可信的进行信息隐藏 public 所有实体都可以访问 protected 只允许本类 和子类 的成员函
  • 【动态规划】---入门(一)

    文章目录 前言 一 斐波那契数列 二 爬楼梯 总结 前言 动态规划入门学习文章记录及总结 动态规划算法的基本思想是 将待求解的问题分解成若干个相互联系的子问题 先求解子问题 然后从这些子问题的解得到原问题的解 对于重复出现的子问题 只在第一
  • 综合案例——手写数字图像处理算法比较

    手写数字图像识别各种算法的比较 1 准备工作 1 1 数据集介绍 使用到了两个手写数字的数据集 scikit learn 中的手写数字数据集 mnist 中的手写数字数据集 1 1 1 scikit learn 中的手写数字数据集 首先来看
  • 清理QT SDK编译后,examples目录中的pdb ilk exp等垃圾文件

    for R s in do del q s s pdb s ilk s exp pause 将上面的代码保存为bat 放到examples根目录下运行 上面的方法是我走的冤枉路 下面的这种方法更简单 在用nmake编译完成后 直接运行nma
  • Bean定义注册机

    org springframework beans factory support BeanDefinitionRegistry 注册Bean定义 org springframework beans factory support Bean
  • docker 安装 oracle

    docker 安装 oracle 拉取镜像 docker pull jaspeen oracle xe 11g 因为版本不同有的可能是jaspeen oracle 11g 运行镜像文件 docker run p 1521 1521 name
  • mysql游标的使用

    p 这是一个游标的使用例子 但是其中有几点需要注意 就是为什么要加入 declare CONTINUE HANDLER FOR SQLSTATE 02000 SET tmpname null 这样的一句话 如果不加的话将直接报错 No da
  • Elasticsearch学习4-数据修改

    数据修改 原文请查看 Modifying Your Data Elasticsearch 提供近乎实时的数据操作和搜索功能 默认情况下 从索引 更新 删除数据到数据出现在搜索结果中 你可以预估一秒钟的延迟 刷新间隔 这是与其他平台 如SQL
  • 波士顿房价(只依据一个特征的)预测

    波士顿房价数据集 波士顿房价数据集中一共有506条数据 涵盖506个不同郊区的房屋数据 在机器学习中 通常要把数据集划分为训练数据集和测试数据集 在波士顿数据库中 默认其中404条是训练数据集 102条作为测试数据集 其中 每条数据有14个
  • arcgis10之矢量数据生成tiff文件

    项目场景 shp数据有时数量过于庞大 不利于操作 但是可以将shp转化成tiff图片导出方便进行下一步操作 解决方案 使用arcgis将shp文件生成tiff即可 具体操作如下 第一步 将shp数据生成栅格数据 具体操作如下图 加载至arc
  • Unable to cast object of type 'System.Decimal' to type 'System.Array'.

    数据库有一个字段AppID 类型定义为numeric 38 0 实体描述如下 Key Column APP ID Required ErrorMessage APP ID不能为空 MaxLength 38 ErrorMessage APP
  • 文件时间对于CP命令的影响

    今天在打包安装包的时候 碰到这样一个问题 打包的服务器时间由于测试需要被改到这个月的月底了 打包的时候我也没有在意这个细节 直接在服务器上重新编译这个代码并且将需要更新的so文件打入tar包里面 tar包的时间 包里面文件的时间都变成这个月
  • 详解如何建立Qt插件学习教程

    如何建立Qt插件学习教程是本文要介绍的内容 主要是来了解QT中插件的应用 如何来建立 文中有详解 具体内容的实现来看详解 QT提供2个API来建立插件 1 高层API扩展QT库 例如定制的数据库驱动 图像格式 字符编码 custom sty
  • 更改远程桌面端口

    远程终端服务是一项功能非常强大的服务 同时也成了入侵者长驻主机的通道 入侵者可以利用一些手段得到管理员账号和密码并入侵主机 这就需要我们来修改默认端口 防范黑客入侵 远程终端服务基于端口3389 入侵者一般先扫描主机开放端口 一旦发现其开放
  • 怎么用计算机输入名字,怎么用电脑起名字

    怎么用电脑起名字 我姓李我老婆姓贺我想给宝宝取个名字 宝宝想跟我 老婆姓贺 最好是叫贺李 怎么通过电脑给宝宝取个高分的名字 输入宝宝出生的年 月 日 时辰 姓什么 男宝宝还是女宝宝 然后查一下就知道了 也可以先找人算一下宝宝属于什么命 命中
  • 虚拟机网络模式设置仅主机模式

    说明 为了更好的模拟生产服务器 可以在本地虚拟机进行相关测试 一般实际的生产服务器是仅能与本地主机或内网相同 所以虚拟机设置固定IP 选择仅主机模式做测试尤为重要 虚拟机网络模式选择Hostonly 即仅主机模式 设置固定IP 和主机相同
  • CentOS 7 修改默认 yum 源

    一 修改CentOS默认yum源为mirrors aliyun com 1 首先备份系统自带的yum源配置文件 root localhost mv etc yum repos d CentOS Base repo etc yum repos
  • android历史记录的搜索页

    保存搜索记录 public void saveSearchHistory String inputText SharedPreferences sp WDApplication getContext getSharedPreferences
  • DoubleCheck验证(双重检查锁和延迟初始化)

    场景 二话不说 直接上代码 public class DoubleCheckDemo private static DoubleCheckDemo demo public DoubleCheckDemo getDemo if demo nu