单例模式下双重校验锁 DCL 的灵魂三问
文章目录
- 前言
- 如何实现一个双重校验锁 DCL
- 定义一个单例变量
- 定义一个获取单例的方法
- 性能优化
- 性能优化带来的一点点问题
- 什么是指令重排?
- 总结
- 如何理解文章开篇理解的三个问题
- 1、为什么需要使用两个 if 语句?
- 2、为什么使用了 synchronized 关键字还需要使用 volatile 关键字?
- 3、双重校验锁使用需要注意的问题
- 个人简介
前言
- hello,大家好,我是 Lorin,今天给大家带来双重校验锁的灵魂三问?以及我们如何一步步实现一个懒汉式单例。开始阅读前,大家可以思考下面三个问题:
DCL 实现中:
1、为什么需要使用两个 if 语句?
2、为什么使用了 synchronized 关键字还需要使用 volatile 关键字?
3、双重校验锁使用需要注意的问题
如何实现一个双重校验锁 DCL
- 双重校验锁 DCL 最常用使用的场景在懒汉式单例,下面我们按照思路简单实现一个懒汉式单例:
定义一个单例变量
public class SingletonDemo {private static Object object = null;
}
定义一个获取单例的方法
- 定义一个单例的获取方法,用于单例的初始化和获取,为了支持多线程访问,我们这里使用 synchronized 进行同步,保证同一时刻只有一个线程访问。
public class SingletonDemo {private static Object object = null;// 初始化和获取实例public Object getObject() {synchronized (SingletonDemo.class) {if (object == null) {object = new Object();}return object;}}
}
性能优化
- 上面的懒汉式单例看起来并没有多大的问题,但是却存在很大的性能的问题,因为我们每次获取我们的实例都需要进行锁的获取和释放,即使我们的实例已经初始化完成,因此为了解决这个问题,我们需要进行一点点优化。
public class SingletonDemo {private volatile static Object object = null;public Object getObject() {// 如果实例已经初始化完成,直接返回实例不获取锁if (object != null){return object;}synchronized (SingletonDemo.class) {if (object == null) {object = new Object();}return object;}}
}
性能优化带来的一点点问题
- 上面的代码表面上看起来已经完美了,解决了并发问题,也优化了性能问题,但是仔细看你会发现了新的问题,由于处理指令重排的优化可能导致 object != null 判断并不准确,怎么理解呢?
创建一个对象分为初始化和实例化两部分,大致可以分为以下几步:1、在堆中申请一份内存
2、创建对象
3、将 object 指向我们对象的内存引用如果没有指令重排的情况下,我们拿到的对象一定是完整的对象,但是可能存在指令重排优化,上面的顺序可能变成下面这样:1、申请一份内存
2、将 object 指向我们对象的内存引用
3、创建对象那么我们将会拿到一个没有实例化完成的对象,因此我们需要禁止指令重排,Java 提供了 volatile 指令来禁止指令重排。
- 题外话:我们写代码的过程其实就是不断在重复优化和解决的问题,直到达到适应我们目前场景、基本情况的最优解(不一定是理论的最优解)。
什么是指令重排?
-
为了提升执行速度/性能,计算机在执行程序代码的时候,会对指令进行重排序。什么是指令重排?简单来说就是系统在执行代码的时候并不一定是按照程序的代码的顺序依次执行。
-
指令重排可以保证单线程串行语义一致(as-if-serial),但是没有义务保证多线程间的语义也一致,所以在多线程下,指令重排可能会导致一些问题。
-
关于指令重排更多内容可以参考 一文读懂 Java Memory Model(JMM)
-
最后,我们得到了终极版本的代码:
public class SingletonDemo {private volatile static Object object = null;public Object getObject() {// 如果实例已经初始化完成,直接返回实例不获取锁if (object != null){return object;}synchronized (SingletonDemo.class) {if (object == null) {object = new Object();}return object;}}
}
总结
如何理解文章开篇理解的三个问题
1、为什么需要使用两个 if 语句?
- 为了性能优化
2、为什么使用了 synchronized 关键字还需要使用 volatile 关键字?
- 性能优化导致带来了多线程指令重排问题,需要使用 volatile 解决指令重排的问题。
3、双重校验锁使用需要注意的问题
- JDK版本大于1.5
- Volatile 屏蔽指令重排序的语义在 JDK1.5 中才被完全修复,此前的 JDK 中即使将变量声明为 volatile 也仍然不能完全避免重排序所导致的问题
- 关于 Volatile 相关介绍可以参考 Volatile 相关章节。
个人简介
👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.
🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。
🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。
💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。
🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。
📖 保持关注我的博客,让我们共同追求技术卓越。
相关文章:
单例模式下双重校验锁 DCL 的灵魂三问
文章目录 前言如何实现一个双重校验锁 DCL定义一个单例变量定义一个获取单例的方法性能优化性能优化带来的一点点问题什么是指令重排? 总结如何理解文章开篇理解的三个问题1、为什么需要使用两个 if 语句?2、为什么使用了 synchronized 关键字还需要使用…...

oracle中关于connect by的语法及实现(前序遍历树)
语法 connect by是是结构化查询中用到的,其基本语法是: 1 select … from tablename 2 start with 条件1 3 connect by 条件2 4 where 条件3; 使用示例 例: create table tree(id int,parentid int); insert into tree values(120,184); …...

学习笔记二十七:K8S控制器Statefulset入门到企业实战应用
这里写目录标题 Statefulset控制器:概念、原理解读Statefulset资源清单文件编写技巧查看定义Statefulset资源需要的字段查看statefulset.spec字段如何定义?查看statefulset的spec.template字段如何定义 Statefulset使用案例:部署web站点State…...
JavaScript 的 闭包
在 JavaScript 中,闭包是一种强大的特性,它允许函数在结束执行后,仍能访问并控制其外部的局部变量。这种特性在许多高级 JavaScript 编程场景中都发挥着关键作用,如创建函数工厂、实现数据隐藏和封装等。 1、闭包的原理 JavaScri…...

二蛋赠书六期:《Linux管理入门经典(第8版)》
前言 大家好!我是二蛋,一个热爱技术、乐于分享的工程师。在过去的几年里,我一直通过各种渠道与大家分享技术知识和经验。我深知,每一位技术人员都对自己的技能提升和职业发展有着热切的期待。因此,我非常感激大家一直…...

19.10 Boost Asio 同步文件传输
在原生套接字编程中我们介绍了利用文件长度来控制文件传输的方法,本节我们将采用另一种传输方式,我们通过判断字符串是否包含goodbye lyshark关键词来验证文件是否传输结束了,当然了这种传输方式明显没有根据长度传输严谨,但使用这…...

微信小程序:两层循环的练习,两层循环显示循环图片大图(大图显示、多层循环)
效果 代码分析 外层循环 外层循环的框架 <view wx:for"{{info}}" wx:key"index"></view> wx:for"{{info}}":这里wx:for指令用于指定要遍历的数据源,即info数组。当遍历开始时,会依次将数组中的每…...

输入几个数,分别输出其中的奇数和偶数
这个问题我们只需要设计几个循环嵌套在一起就可以解决,话不多说,我们直接上代码 目录 1.运行代码 2.运行结果 1.运行代码 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<string.h>int main() {int arr[10] {1,2,3,4,5,6,…...
香港Web3.0:从政策到实践,探索未来发展路径
随着互联网技术的快速发展,互联网正在经历从Web1.0到Web3.0的重大升级。在这场互联网新技术革命的浪潮中,谁能抓住机遇,谁就能成为未来的引领者。 2022年11月,香港政府发布了《有关香港虚拟资产发展的政策宣言》,彰显…...

Java程序员面试核心知识--Java基础知识(一)
目录 一、Java程序初始化顺序 二、Java的Clone方法作用 三、 OverLoad(重载)与Override(重写)区别 四、abstract class(抽象类)与interface(接口)的异同 五、String、StringBuf…...

Linux的test测试功能
测试文件名的类型,文件是否存在, 文件的权限检测 文件之间的比较 两个整数之间的比较 判断字符串数据 多重条件判定 一个一个来,这个有点多,不过比较有意思,来代码 案例1,判断文件是否存在ÿ…...

为什么看了那么多测试技术帖,感觉自己还是菜?
作为测试新手,最爱莫过于看各大牛发的技术贴,这篇很牛叉,那篇也很有道理,似乎自己看着看着也会成为高手。然而几年后,发现自己对专业知识的理解乱的很,里面更有很多自相矛盾的地方,这到底是哪里…...

HTML和CSS的基础-前端扫盲
想要写出一个网页,就需要学习前端开发(写网页代码)和后端开发(服务器代码)。 对于前端的要求,我们不需要了解很深,仅仅需要做到扫盲的程度就可以了。 写前端,主要用到的有…...

Flutter 02 基础组件 Container、Text、Image、Icon、ListView
一、Container容器组件: demo1: import package:flutter/material.dart;void main() {runApp(MaterialApp(home: Scaffold(appBar: AppBar(title: const Text("你好Flutter")),body: const MyApp(),),)); }// 容器组件 class MyApp extends St…...
[笔记] 字符串输入 #字符输入
字符串的多组输入格式 scanf("%c", &ch)读取单个字符,用EOF作为结束的判断标志。 刷题记录:[题] 查找最大元素 #字符输入 逐个字符手动读取,因为题目的要求,要对每个字符逐个操作,所以就输入的时候顺便…...

服务器数据恢复—EMC存储pool上数据卷被误删的数据恢复案例
服务器数据恢复环境: EMC Unity某型号存储,连接了2台硬盘柜。2台硬盘柜上创建2组互相独立的POOL,2组POOL共有21块520字节硬盘。21块硬盘组建了2组RAID6,1号RAID6有11块硬盘. 2号RAID6有10块硬盘。 服务器故障&检测࿱…...
记录一次@Slf4j log.info 日志信息未输出到日志文件的问题
Spring Boot的起步依赖(如spring-boot-starter-web)中已经包含了Slf4j的依赖,无需额外添加。: <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artif…...
Git 使用规范流程
开发中使用Git流程 参考文章:阮一峰- Git 使用规范流程 开发新功能:应该新建一个单独的分支(这方面可以参考《Git分支管理策略》)。提交分支commit:分支修改后,就可以提交commit了。提交时,应遵…...

69 内网安全-域横向CobaltStrikeSPNRDP
目录 演示案例:域横向移动RDP传递-Mimikatz域横向移动SPN服务-探针,请求,导出,破解,重写域横向移动测试流程一把梭哈-CobaltStrike初体验 涉及资源 SPN主要是扫描技术,在渗透过程中结合kerberos协议,可以做一些事情 演示案例: 域横向移动RDP传递-Mimik…...

GB28181学习(十四)——语音广播与语音对讲
语音对讲 定义 用户端向设备通过视音频点播请求音频数据;用户端接收音频数据并通过特定的播放设备(如音响)播放;用户端向设备发送广播请求;设备解析广播成功后通过INVITE方法向用户请求音频数据;用户通过音…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
反向工程与模型迁移:打造未来商品详情API的可持续创新体系
在电商行业蓬勃发展的当下,商品详情API作为连接电商平台与开发者、商家及用户的关键纽带,其重要性日益凸显。传统商品详情API主要聚焦于商品基本信息(如名称、价格、库存等)的获取与展示,已难以满足市场对个性化、智能…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
C++ 基础特性深度解析
目录 引言 一、命名空间(namespace) C 中的命名空间 与 C 语言的对比 二、缺省参数 C 中的缺省参数 与 C 语言的对比 三、引用(reference) C 中的引用 与 C 语言的对比 四、inline(内联函数…...
解决本地部署 SmolVLM2 大语言模型运行 flash-attn 报错
出现的问题 安装 flash-attn 会一直卡在 build 那一步或者运行报错 解决办法 是因为你安装的 flash-attn 版本没有对应上,所以报错,到 https://github.com/Dao-AILab/flash-attention/releases 下载对应版本,cu、torch、cp 的版本一定要对…...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
高防服务器能够抵御哪些网络攻击呢?
高防服务器作为一种有着高度防御能力的服务器,可以帮助网站应对分布式拒绝服务攻击,有效识别和清理一些恶意的网络流量,为用户提供安全且稳定的网络环境,那么,高防服务器一般都可以抵御哪些网络攻击呢?下面…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...