单例模式下双重校验锁 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编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

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

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

相关文章

AI 引擎系列 4 - 首次运行 AI 引擎编译器和 x86simulator(2022.1 更新)

AI 引擎系列 4 - 首次运行 AI 引擎编译器和 x86simulator(2022.1 更新) 简介 在 AI 引擎系列的前 3 篇博文中,我们探讨了 AI 引擎应用所需的不同文件。在本篇中,我们将为 X86 目标运行 AI 引擎编译器,观察它生成的不…

二进制搭建 Kubernetes(K8s)

目录 1、环境 1.1 操作系统初始化配置 1.2 部署 docker引擎 1.3 部署 etcd 集群 1.4 准备签发证书环境 1.5 部署 Master 组件 1.6 部署 Worker Node 组件 1.7 部署 CNI 网络组件 1.7.1 部署 flannel 1.7.2 部署 Calico 1.7.3 node02 节点部署 1.7.4 部署 CoreDNS 1…

0-1背包 完全背包 + 至多/恰好/至少 + 空间优化 + 常见变形题(实战力扣题)

(一)01背包 1.回溯三问 # capacity:背包容量 # w[i]: 第 i 个物品的体积 # v[i]: 第 i 个物品的价值 # 返回:所选物品体积和不超过 capacity 的前提下,所能得到的最大价值和 def zero_one_knapsack(capacity:int,w:List[int],v:List[int])…

【Linux】第八站:gcc和g++的使用

文章目录 一、解决sudo命令的问题二、Linux编译器-gcc/g1.gcc的使用2.g的使用 三、gcc编译链接过程1.预处理2.编译(生成汇编)3.汇编(生成机器可识别代码)4.链接(生成可执行文件或库文件)5.一些选项的意义 四…

决策树算法的实现

决策树是一种机器学习算法,它类似于人脑思考问题的过程。我们可以通过问一系列的问题来逐步缩小答案的范围,最终得到最终的答案。 比如说,我们想要预测一个人是否会购买某个产品,我们可以通过一系列的问题来缩小预测范围。例如&a…

Docker容器技术实战3

8、docker原生网络 Docker原生网络基于Linux桥接技术和虚拟网络接口,使用了Linux内核的网络功能。每个Docker容器都有自己的网络命名空间,这使得容器之间可以使用独立的IP地址,并隔离了容器的网络栈。 当创建一个Docker原生网络时&#xff…

Airtest工具根据App页面文字信息提取坐标进行截图保存在自定义文件夹

Airtest工具根据App页面文字信息提取坐标进行截图保存在自定义文件夹 一、项目背景 在一个项目中,选项被选中和未选中的节点元素的属性值无变化,通过AI识别率达不到百分百,想着通过计算图片的HSV值来判断选择能否被选中。(HSV比…

ESP32 for Arduino 分区信息

忘记过去,超越自己 ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2023-11-04❤️❤️ 本篇更新记录 2023-11-04❤️🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝🙏…

【JAVA学习笔记】59 - JUnit框架使用、本章作业

项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter15/src/com/yinhai/homework JUnit测试框架 1.基本介绍 1. JUnit是一个Java语言的单元测试框架 2.多数Java的开发环境都已经集成了JUnit作为单元测试的工具 2.如何使用 创建方法后&#x…

QtC++与QVariant详解

什么是 QVariant? QVariant 是 Qt 中的一个类,允许您以一种通用的方式存储和访问数据,即使这些数据具有不同的数据类型。它在许多情况下非常有用,例如在模型视图编程、数据序列化、设置和配置管理等领域。QVariant 可以包含以下类…

JVM离线分析-使用MAT分析dump堆文件

1. MAT(Memory Analyzer Tool)的介绍 官方介绍 The Eclipse Memory Analyzer is a fast and feature-rich Java heap analyzer that helps you find memory leaks and reduce memory consumption. Use the Memory Analyzer to analyze productive heap …

Java随机获取某个范围内的随机整数

随机获取某个范围内的随机整数 一、代码 /*** 随机获取某个范围内的随机整数的值* param min 最小值* param max 最大值* return*/public static int randomNum(int min,int max) {// 创建一个Random对象Random random new Random();// 生成指定范围内的随机整数int randomI…

通过@ConfigrationProperties读取配置文件属性并赋值

这种设计使得 Anything 成为 Something 类的静态成员,因此不依赖于外部类的实例。静态内部类通常更独立,它们可以单独存在且无需引用外部类的实例。 如果将 Anything 类定义为非静态的内部类,它将依赖于 Something 类的实例。这意味着每个 S…

牛客周赛 Round 17

A、 题目描述: 游游拿到了一个边长为n的正方形披萨,她准备切k刀(每刀只能横着或竖着切开),最终会形成若干个小矩形披萨。游游希望每个小披萨的面积相等,且矩形的长和宽的差的绝对值尽可能小。你能帮游游求出…

vue中的rules表单校验规则使用方法 :rules=“rules“

一、el-form里面必写属性值 :ref"dataForm" // 提交表单时进行校验 :rules"rules" // return 下的校验规则 :model"userForm" // 绑定表单的值 <el-formref"dataForm" // 必写属性值:rules"rules"…

CV论文阅读大合集

YearNameAreamodeldescriptiondrawback2021 ICMLClip &#xff08;Contrastive Language-Image Pre-training&#xff09;contrastive learning、zero-shot learing、mutimodel用文本作为监督信号来训练可迁移的视觉模型CLIP’s zero-shot performance, although comparable to…

Eureka处理流程

1、Eureka Server服务端会做什么 1、服务注册 Client服务提供者可以向Server注册服务&#xff0c;并且内部有二层缓存机制来维护整个注册表&#xff0c;注册表是Eureka Client的服务提供者注册进来的。 2、提供注册表 服务消费者用来获取注册表 3、同步状态 通过注册、心跳机制…

海康多相机同步取流保存图片

话不多说&#xff0c;直接上代码。代码里包含了多窗口显示图像和保存图片。 #"rtsp://admin:123456qq192.168.10.192/stream1" # rtsp://admin:Admin123192.168.100.103:554/Streaming/Channels/101 #rtsp://admin:Admin123192.168.100.103:554/cam/realmonitor?ch…

redis缓存击穿,redisson分布式锁,redis逻辑过期

什么是缓存击穿&#xff1a; 缓存击穿是指在高并发环境下&#xff0c;某个热点数据的缓存过期&#xff0c;导致大量请求同时访问后端存储系统&#xff0c;引起系统性能下降和后端存储压力过大的现象。 解决方案&#xff1a; 1. redisson分布式锁 本质上是缓存重建的过程中&…

「Verilog学习笔记」四选一多路器

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 分析 通过波形示意图我们可以发现&#xff0c;当sel为0&#xff0c;1&#xff0c;2时&#xff0c;输出mux_out分别为d3&#xff0c;d2&#xff0c;d1&#xff0c;那么sel3…