单例模式中的双重锁详解

Java中双重检查锁(double checked locking) 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。双重检查锁(Double-Check Locking),顾名思义,通过两次检查,并基于加锁机制,实现某个功能。// 此处如果有多个执行流同时进入,会造成多次初始化// 第1次,一般性检查,但是有并发隐患:可能有多执行流同时进入改处// 此处第2次检查,为了防止后续多执行流并发时,后续获取同步锁的执行流,不会再次初始化Singleton对象。 阅读详情
AI摘要

本文详细解读了双重检查锁定的单例模式,如何确保线程安全,避免重排序问题,以及使用volatile关键字的重要性。通过实例分析和原理讲解,探讨了其在多线程环境下的高效实现和潜在隐患的解决策略。

双重检查

public class SingletonExample {
    private volatile static SingletonExample singletonExample;
    public SingletonExample getInstance(){
        if (singletonExample==null){
            synchronized (SingletonExample.class){
                if (singletonExample==null){
                    singletonExample=new SingletonExample();
                }
            }
        }
        return singletonExample;
    }
}

上面是双重检查的正确代码,这样写是为了解决以下问题:

  1. 保证对象是单例的。

  2. 保证synchronized只在第一次调用,节省开销。

  3. 防止了重排序带来的隐患。

下面我们将来逐步分析。

不考虑多线程时:
public class SingletonExample {
    private static SingletonExample singletonExample;
    public SingletonExample getInstance(){
        if (singletonExample==null){
            singletonExample=new SingletonExample();
        }
        return singletonExample;
    }
}

在多线程情况下,这样写会导致多个实例:

TimeThread AThread B
T1检查到uniqueSingleton为空
T2检查到uniqueSingleton为空
T3初始化对象A
T4返回对象A
T5初始化对象B
T6返回对象B
解决多实例方法:加锁
public class SingletonExample {
    private static SingletonExample singletonExample;
    public SingletonExample getInstance(){
        synchronized (SingletonExample.class){
            if (singletonExample==null){
                singletonExample=new SingletonExample();
            }
        }
        return singletonExample;
    }
}

加锁之后,多个实例的问题就被解决了,但是会产生一个新问题,synchronized会被多次使用,但实际上只需要在第一次实例化时使用。

双重检查锁
public class SingletonExample {
    private static SingletonExample singletonExample;
    public SingletonExample getInstance(){
        if (singletonExample==null){
            synchronized (SingletonExample.class){
                if (singletonExample==null){
                    singletonExample=new SingletonExample();
                }
            }
        }
        return singletonExample;
    }
}

如果这样写,运行顺序就成了:

  1. 检查变量是否被初始化(不去获得锁),如果已被初始化则立即返回。

  2. 否则获取锁。

  3. 再次检查变量是否已经被初始化,如果还没被初始化就初始化一个对象。

执行双重检查是因为,如果多个线程同时了通过了第一次检查,并且其中一个线程首先通过了第二次检查并实例化了对象,那么剩余通过了第一次检查的线程就不会再去实例化对象。

这样,除了初始化的时候会出现加锁的情况,后续的所有调用都会避免加锁而直接返回,解决了性能消耗的问题。

隐患

重排序造成的隐患

上述写法看似解决了问题,但是有个很大的隐患。实例化对象的那行代码(标记为error的那行),实际上可以分解成以下三个步骤:

  1. 分配内存空间

  2. 初始化对象

  3. 将对象指向刚分配的内存空间

但是有些编译器为了性能的原因,可能会将第二步和第三步进行重排序,顺序就成了:

  1. 分配内存空间

  2. 将对象指向刚分配的内存空间

  3. 初始化对象

现在考虑重排序后,两个线程发生了以下调用:

TimeThread AThread B
T1检查到SingletomExample为空
T2获取锁
T3再次检查到SingletomExample为空
T4为SingletomExample分配内存空间
T5将SingletomExample指向内存空间
T6检查到SingletomExample不为空(此时虽然地址不为空,但却未初始化)
T7访问SingletomExample(此时对象还未完成初始化)
T8初始化SingletomExample

在这种情况下,T7时刻线程B对uniqueSingleton的访问,访问的是一个初始化未完成的对象。

正确的双重检查锁
public class SingletonExample {
    private volatile static SingletonExample singletonExample;
    public SingletonExample getInstance(){
        if (singletonExample==null){
            synchronized (SingletonExample.class){
                if (singletonExample==null){
                    singletonExample=new SingletonExample();
                }
            }
        }
        return singletonExample;
    }
}

为了解决上述问题,需要在uniqueSingleton前加入关键字volatile。使用了volatile关键字后,重排序被禁止,所有的写(write)操作都将发生在读(read)操作之前。

至此,双重检查锁就可以完美工作了。

单例模式(双重检测锁式单例不安全的原因与解决) 单例模式 核心作用 保证一个类只有一个对象,并且提供一个访问该实例的全局访问点 单例模式的优点 由于单例模式只生成一个实例,减少了系统的开销,当一个对象需要比较的多的资源时,如读取配置、产生其它依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决 单例模式可以在系统设置全局访问点,优化环共享资源访问,例如可以设计一个单例类,负责所以数据表的映射处理 常见的五... 阅读详情

相关推荐

【地铁上的设计模式】--创建型模式:单例模式(三)--双重检验锁单例

双重检验锁单例模式实现了懒汉式单例模式的延迟加载和饿汉式单例模式的线程安全。其主要思路是在获取单例实例时,先检查是否已经实例化,如果没有才进行同步块。在同步块内部再进行一次实例化检查,以确保只有一个实例被创建。这样,就能够在保证单例实例唯一性的同时,减少了多线程环境下的性能开销。双重检验锁单例模式能够确保只有一个实例被创建,并具有较好的性能表现和延迟创建的能力。其优点是在多线程环境下可以保证线程安全,并且可以延迟对象的创建,节省了系统资源。

喵叔 467

volatile引出的单例模式之双重检查

volatile 作用!!!! 保证线程可见性(MESI,利用CPU的缓存一致性协议) 禁止指令重排序(CPU) 单例模式之双重检查 在实现单例模式时,如果未考虑多线程的情况,就容易写出下面的错误代码: public class Singleton { private static Singleton uniqueSingleton; private Singleton() { } public Singleton getInstance() { if

weixin_41209919的博客 735

Yakit实战:利用验证码识别插件突破Webfuzzer自动化测试瓶颈

本文详细介绍了如何利用Yakit的Webfuzzer模块结合验证码识别插件,突破自动化安全测试中的验证码瓶颈。通过实战演示,讲解了如何部署本地OCR服务、编写热加载脚本,实现从获取验证码到自动识别并替换的全流程自动化,从而让Webfuzzer能够高效、无人值守地进行大规模密码喷洒等测试。

weixin_29323493的博客 283

单例模式之「双重校验锁」

/ 加 volatileif (INSTANCE == null) { // 双重校验:第一次校验synchronized(Lock2Singleton.class) { // 加 synchronizedif (INSTANCE == null) { // 双重校验:第二次校验过程如下:判断 INSTANCE 是否为null,检查变量是否被初始化(不去获得锁),如果已被初始化立即返回这个变量;不为null,直接返回,不用去竞争锁。

IT枫斗者的博客 1222

HDMI设计2----EDID and E-EDID

1,EDID是什么?有什么用? 图1 应用场景 如图1所示的应用场景,PC将视频信号发送 到我们的设备上,经过一系列的传输和切换,设备将视频信号发送到显示器或者投影机上,也就是说,我们的设备位于PC和显示器之间。首先,在输入端,设备需要让PC知道自己是能够接受输入视频信号的,让PC认为自己是一台显示器。如果不这样做的话,PC可能会拒绝输出任何视频信号的(VGA接口不需要EDID也能输出)。除此之外,设备还要告诉PC自己支持的显示时序。而在输出端,当设备需要自建输出时序而不是简单的复制...

Archar_Saber的博客 7990

EFR32BG22 Thunderboard开发板 使用SPI 读取加速度计

Simplicity Studio v5 创建Silicon Labs蓝牙SoC EFR32BG22 Thunderboard模块的加速度计工程,模块上有一个ICM-20648 6轴运动跟踪设备,集成3轴加速度计和3轴陀螺仪功能,使用SPI接口跟EFR32BG22芯片连接。 我尽量以图片的形式展示(少打字): 将EFR32BG22的Thunderboard模块通过USB线插入到电脑,打开Simplicity Studio v5软件。 下面看重点: 点击搜索到的IO Stream: USART,点击Ins

qq_25727979的博客 1494

单例模式 - 双重检查锁定(Double-Checked Locking)

介绍单例模式、双重检查锁定(double-checked locking)

qq_44960878的博客 4329

2025年有道翻译网页版JS逆向实战:从抓包到AES解密,手把手教你搞定sign签名

本文详细解析2025年有道翻译网页版的JS逆向实战,从抓包分析到AES解密全流程。通过实战演示签名生成机制和AES-CBC解密技术,帮助开发者破解有道翻译的反爬系统,实现高效数据获取。重点涵盖动态密钥处理、错误重试机制及反反爬策略,为开发者提供完整的技术解决方案。

weixin_30379531的博客 505

单例模式三种模式,饿汉、饱汉、双重锁模式,实例及优劣详解

单例模式是一种很基础的设计模式,在面试时可能会被要求手写不同类型的单例模式代码,主要有三种模式: 1、饿汉模式: //饿汉模式,很饿很着急,所以类加载时即创建实例对象 public class Singleton1 { private static Singleton1 singleton = new Singleton1(); private Singleton1(){

Leeon的博客 2万+

基于深度学习的农作物病虫害检测识别系统:YOLOv8 + UI界面 + 数据集

该系统的目标是使用YOLOv8模型自动检测农作物的病虫害,并通过UI界面提供实时反馈。用户可以通过上传图像,系统会输出图像中是否存在病虫害以及具体种类。数据集准备与处理:收集并准备包含农作物病虫害图像的数据集,并进行标注和数据增强。YOLOv8模型训练:使用YOLOv8训练农作物病虫害检测模型。UI界面设计与开发:开发一个用户友好的界面,支持上传图像并展示检测结果。系统测试与优化:评估模型的准确性、实时性,并对系统进行优化。

YOLO 883

uniapp 开发小程序 使用 `uni.canvasToTempFilePath` 导出图片时遇到的 “fail fail canvas is empty“ 错误解决方法

问题的关键在于,在调用时需要正确的上下文对象。如果没有传入this(vue2)或instance(vue3),就会导致导出失败,提示错误。这也提醒我们,在使用类似这样的 API 时,要确保上下文传递正确,以便函数能够正常执行。希望这个排查过程和解决方案能帮助你解决类似的问题!

代码简单说 Vue、JAVA、PHP、Node.js 熟练运用,接口、架构、性能全搞定。 2331

C++设计模式--单例模式详解(懒汉模式、饿汉模式、双重锁)

单例模式

u013083044的博客 1701

VisionPro - 基础 - 模板匹配技术-Search/PMAlign/PatMax(6)-纹理屏蔽和重叠匹配

Note:对已经有了,1已经训练好的模板,2一组使能的自由度,3一个待测的实时图片,PatMax 就能模板匹配的给出变化的最佳描述。对一些综合的模板,这三个输入值往往是一起影响最后的变换结果信息。例如,如果我们训练了一个圆形的模板,然后搜索一个角度自由度包括一个圆,那么将会有无限个可能的角度满足要求。这些组合的模板匹配,被官方名为:退化系统,a degenerate system的结果被叫做a degenerate result 退化结果4.2 退化系统例子。

山云的专栏 1534

单例模式详解(懒汉式、饿汉式、线程安全、双重检验锁)

单例模式详解(懒汉式、饿汉式、线程安全、双重检验锁) 单例(Singleton)概述 使单例模式是为了:保证一个类只有一个实例,并且提供这个实例的全局访问点 一般做法是把该单例实例作为该类的一个静态变量、构造方法私有化,至于如何取得和创建该对象,有两种种方法实现:懒汉式和饿汉式 Ⅰ 懒汉式-线程不安全 这个实现在多线程环境下是不安全的,如果多个线程能够同时进入 if (uniqueInstance...

weixin_43217564的博客 3555

组基轨迹建模 GBTM的介绍与实现(Stata 或 R)

组基轨迹建模(Group-Based Trajectory Modeling,GBTM)(旧名称:Semiparametric mixture model)历史:由DANIELS.NAGIN提出,发表文献《Analyzing Developmental Trajectories:A Semiparametric,Group-Based Approach》GBTM能够将一群人的轨迹分类并生成数个具有代表性的运动轨迹模型,然后对每个轨迹模型进行分析,以了解人们的运动特征、生理水平和风险等级等。

三分钟热度 1万+

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

C++设计模式之单例模式详解(懒汉模式、饿汉模式、双重锁)

有时,我们希望类的实例对象有且仅有一个,比如某个页面,我们希望它如果出现,永远只有一个,那么此时你可能就需要用到单例模式了。(PS:本人亲身经历过手写单例模式的面试,所以以下代码必须能够手撕!!!) 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点。首先,怎么让类只有一个实例呢?肯定是构造函数需要做点“手脚”了,如果构造函数一如既往的是public属性,那还是可以任意构造对象,则不满足要求了。所以大神们想出来将构造函数私有化的方法,即把构造函数设置成私有属性,并对外提供一...

trackxiaoxin321的博客 1万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

面试题:单例模式三种模式,饿汉、饱汉、双重锁模式,实例及优劣详解 背过1...

单例模式是一种很基础的设计模式,在面试时可能会被要求手写不同类型的单例模式代码,主要有三种模式: 1、饿汉模式: [java]view plaincopy //饿汉模式,很饿很着急,所以类加载时即创建实例对象 publicclassSingleton1{ privatestaticSingleton1sing...

weixin_33911824的博客 199

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

第11篇:SkillRegistry 单例模式的双重检查锁定实现细节

SkillRegistry 采用双重检查锁定模式确保单例线程安全,使用两把锁分别保护实例创建和注册表操作。类级别 _lock 保证单例初始化唯一性,实例级别 _registry_lock 保护技能字典和分类字典的并发读写。关键设计包括: 快速路径优先检查实例存在避免锁竞争 锁内二次检查防止多实例创建 注册操作通过锁保证"检查-写入-索引"三步原子性 查找操作获取技能引用后立即释放锁 调用执行分离锁保护与业务逻辑 这种设计在保证线程安全的同时兼顾性能,适用于高并发的技能注册与调用场景。

u010972645的博客 308

双重锁的单例模式

首先volatile修饰这个single,保证多线程可见。其次双重锁:对single == null 进行两次判空。

2302_80742310的博客 311

Java 双重锁校验

双重锁校验单例 什么是单例模式? 单例保证一个对象JVM中只能有一个实例 /** * 双重锁校验的单例 */ public class DoubleLock implements Serializable{ public static volatile DoubleLock doubleLock = null;//volatile防止指令重排序,内存可见 //(缓存中的变化及时刷到主存,并且其他的内存失效,必须从主存获取) private DoubleLock(){

白的博客 495

Java单例---双重锁校验

双重锁校验单例 什么是单例模式? 单例保证一个对象JVM中只能有一个实例 /** * 双重锁校验的单例 */ public class DoubleLock implements Serializable{ public static volatile DoubleLock doubleLock = null;//volatile防止指令重排序,内存可见 //(缓存中的变化及时刷到主存,并且其他的内存失效,必须从主存获取) private DoubleLock(){

weixin_44378050的博客 298

面试题:单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单例模式的原理呗!

在某些情况下,JVM 可能会对指令进行重排序,导致在同步块外的线程获取到一个尚未初始化完成的实例。3. **第二次检验(内部 if 语句):** 在同步块内,再次检验实例是否为 null。2. **同步块(synchronized):** 如果实例不存在(即第一次检验失败),则进入同步块。1. **第一次检验(外部 if 语句):** 如果实例已经存在,直接返回该实例。4. **实例创建:** 如果在同步块内检验发现实例仍然为 null,那么创建一个新的实例。**双重检验锁方式的实现原理:**

Vickyqiickyk的博客 174
上一篇: Java集合详解
下一篇: Java实现人脸识别(各项目结构都有案例说明)
高冷小伙
博客等级 码龄7年 223粉丝 182原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

高冷小伙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值