Java Double-Checked Locking 已死,C++ 呢?

已经有众多文章讨论 double-checked locking 模式在 Java 下面无法正常工作,这里先简要的总结一下。

根本原因在于 Java 的 memory model 允许所谓的 out-of-order write ,对于下面的 Java 代码,out-of-order write 可能导致灾难性的结果

public static Singleton getInstance()

{

  if (instance == null)

  {

    synchronized(Singleton.class) {  //1

      if (instance == null)          //2

        instance = new Singleton();  //3

    }

  }

  return instance;

}
 

 

问题的起因在于语句 //3 ,JIT 所生成的汇编代码所作的事情并不是先生成一个 Singleton 对象,然后将其地址赋予 instance 。相反,它的做法是

1. 先申请一块空内存
2. 将其地址赋予 instance
3. 在 instance 所指的地址之上构建对象

下面的汇编代码提供了证明,说明这不只是一个脑筋急转弯,而是实际发生在 JIT 里面的。代码来自 Peter Haggar 的文章,我只是引用一下。

054D20B0   mov         eax,[049388C8]          ;load instance ref
054D20B5   test          eax,eax                         ;test for null
054D20B7   jne           054D20D7
054D20B9   mov         eax,14C0988h
054D20BE   call          503EF8F0                    ;allocate memory
054D20C3   mov         [049388C8],eax          ;store pointer in
                                                                              ;instance ref. instance 
                                                                              ;non-null and ctor
                                                                              ;has not run
054D20C8   mov         ecx,dword ptr [eax]
054D20CA   mov         dword ptr [ecx],1         ;inline ctor - inUse=true;
054D20D0   mov         dword ptr [ecx+4],5     ;inline ctor - val=5;
054D20D7   mov         ebx,dword ptr ds:[49388C8h]
054D20DD   jmp         054D20B0

其中地址为 054D20BE 的代码正在分配内存,而接下来的一行将其赋予 instance ,这个时候 Singleton 的构造函数根本就还没有被调用。

那 么问题在哪里?如果线程调度发生在 instance 已经被赋予一个内存地址,而 Singleton 的构造函数还没有被调用的微妙时刻,那么另一个进入此函数的线程会发觉 instance 已经不为 null ,从而放心大胆的将 instance 返回并使用之。但是这个可怜的线程并不知道此时 instance 还没有被初始化呢!

症结在于:首先,构造一个对象不是原子操作,而是可以被打断的;第二,更重要的,Java 允许在初始化之前就把对象的地址写回,这就是所谓 out-of-order 。

那么,对于 C++ 呢?典型的 C++ double-checked locking 可能是这样的
  

static Singleton* getInstDC()
    {
        if(inst_ == 0)
        {
            boost::mutex::scoped_lock l(guard_);
            if(inst_ == 0)
                inst_ = new Singleton();
        }
        return inst_;
    } 

 

正 如 Java 的行为取决于 JIT 的处理方式,C++ 程序的行为要由编译器来决定。如果某个编译器的处理与 JIT 类似,那么 C++ 程序员也只好对 double-checked locking 说再见。下面是 VC7.1 在 release 配置下生成的代码:

    static Singleton* getInstDC()
    {
00401110  mov         eax,dword ptr fs:[00000000h]
00401116  push        0FFFFFFFFh
00401118  push        offset __ehhandler$?getInstDC@Singleton@@SAPAV1@XZ (4095F8h)
0040111D  push        eax 
        if(inst_ == 0)
0040111E  mov         eax,dword ptr [Singleton::inst_ (40D000h)]
00401123  mov         dword ptr fs:[0],esp
0040112A  sub         esp,8
0040112D  test        eax,eax
0040112F  jne         Singleton::getInstDC+6Eh (40117Eh)
        {
            boost::mutex::scoped_lock l(guard_);
00401131  mov         ecx,offset Singleton::guard_ (40D004h)
00401136  mov         dword ptr [esp],offset Singleton::guard_ (40D004h)
0040113D  call        boost::mutex::do_lock (401340h)
00401142  mov         byte ptr [esp+4],1
            if(inst_ == 0)
00401147  mov         eax,dword ptr [Singleton::inst_ (40D000h)]
0040114C  test        eax,eax
0040114E  mov         dword ptr [esp+10h],0
00401156  jne         Singleton::getInstDC+57h (401167h)
                inst_ = new Singleton();
00401158  push        1   
0040115A  call        operator new (4011A2h)
0040115F  add         esp,4
00401162  mov         dword ptr [Singleton::inst_ (40D000h)],eax
        }
00401167  mov         ecx,offset Singleton::guard_ (40D004h)
0040116C  mov         dword ptr [esp+10h],0FFFFFFFFh
00401174  call        boost::mutex::do_unlock (401360h)
        return inst_;
00401179  mov         eax,dword ptr [Singleton::inst_ (40D000h)]
    }
0040117E  mov         ecx,dword ptr [esp+8]
00401182  mov         dword ptr fs:[0],ecx
00401189  add         esp,14h
0040118C  ret              

从标记为红色的那一句,我们看到了希望:对 inst_ 的赋值发生在 new 完成之后,这意味着至少在 VC7.1 中,我们尚且可以放心使用 double-checked locking ,尽管它未必具有可移植性。

 

一个替代方法:

class SingletonTest
{
private static SingletonTest instance;

private SingletonTest
{
}

private static class Instance//嵌套类只加载一次。
{
static fianl SingletonTest Instance=new SingletonTest();
}

public static SingletonTest()
{
return Instance.instance;
}
}
 

 

【AI理论学习】手把手推导扩散模型:Diffusion Models(DDPM) 生成扩散模型DDPM如下图所示分为前向、逆向两个过程,它首先通过不断往原始清晰数据中添加噪声使其变成标准高斯噪声(前向过程),而后期望从标准高斯噪声中还原原始数据(逆向过程)。若能实现,那便可从已知的标准高斯分布中采样一个噪声数据,而后利用DDPM模型生成符合原始数据分布的新数据。 Diffusion模型包含两个过程:数学符号:y∝xy\propto xy∝x:y正比于x,即y随着x增大而线性增大。 条件概率:贝叶斯公式:p(A∣B)=p(B∣A)×p(A)p(B)p(A|B)=\frac{p(B|A)\ 阅读详情

相关推荐

Wafer的T7 Code是什么,怎么来的

在半导体Cim系统中,waferstart 业务需要支持 Sorter 读取 Wafer 的 T7 Code,与 MES 的 Wafer ID 进行绑定,以便向 前追溯 Wafer 历史,那么问题来了,T7code 是什么Wafer的T7 Code是一种,用于标识双面抛光的晶圆。这种标记采用二维矩阵代码符号,适用于各种尺寸的晶圆,不仅限于450mm无缺口晶圆7。T7 Code是衬底来料背面通常会有的供应商刻的追溯ID,这是一串数字和字母组成的流水码。

阿拉伯梳子的专栏 7924

Java Double-Checked Locking 已死,C++ 呢?

 double-checked locking 模式在 Java 下面无法正常工作,这里先简要的总结一下。  根本原因在于 Java 的 memory model 允许所谓的 out-of-order write ,对于下面的 Java 代码,out-of-order write 可能导致灾难性的结果  public static Singleton getInstance()  {   if (

技术理想 929

一体化伺服电机与施耐德PLC TM241CEC24T在Canopen协议下的应用

一.硬件组成 软件组成:施耐德SoMachineV4.3二.○1.打开SoMachineV4.3,点击“新建项目”○2.点击“空白项目”○3.项目命名○4.点击“创建项目” 三. 点击“Logic Builder” 四. ○1.右击项目”PMM_CANOPEN_ST”添加设备○2.点击”LogicController”选择PLC型号TM241CEC24T/U○3.点击“添加设备” 五. ○1.右击CAN_1,添加设备○2.选择“CANopen Perfomance”○3.点击“添加设备”

m0_46512607的博客 2520

线程同步之利器(2)——区域锁(Scoped locking)

区域锁(Scoped locking)不是一种锁的类型,而是一种锁的使用模式(pattern)。实际上是RAII模式在锁上面的具体应用。本文具体介绍了一下区域锁的实现。

邹鑫的专栏 1万+

Unity插件——WebView内嵌网页/浏览器插件

一、相关插件: 1、Embedded Browser 插件(文件夹名ZFBrowserUnity) 2、UniWebView插件 3、UnityAndroidWebviewToTexture插件 4、3D WebView for Android插件 二、遇到的问题: 3D WebView显示问题 项目中使用3D WebView存在的问题 三、插件使用 Embedded Browser 加载页面的使用

青岚岁叶的博客 1万+

π_RL:解锁流匹配VLA强化学习后训练的关键技术与实战解析

本文深入解析了π_RL框架如何通过流匹配(flow matching)与强化学习(RL)的结合,解决视觉语言动作模型(VLA)在后训练中的关键挑战。详细介绍了Flow Noise和Flow SDE两大核心技术,以及它们在机器人控制任务中的实战效果,显著提升了模型的性能、数据效率和泛化能力。

weixin_29061531的博客 220

双重检查锁(double-checked locking)

在软件工程中,双重检查锁(double-checked locking[1])是一种设计模式,通过在加锁前检查锁标志(criterion),可以减少加锁的开销。只有当标志显示需要的时候才...

felix021的专栏 4342

1.2、基于卷积神经网络(CNN)的车牌自动识别系统(matlab)

基于卷积神经网络(CNN)的车牌自动识别系统(matlab)

XU157303764的博客 2911

双重检查锁(Double-Checked Locking)的缺陷

双重检查锁(Double-Checked Locking)被广泛用于多线程环境下实现延迟初始化实例,但是它其实是有缺陷的。

极客星云-优快云博客 6767

08 RK3568 MAC TO MAC直连 RTL8367S mdio

本文:采用rk3568 gamc0 通过rgmii直连 交换芯片的mac口 通信方法为mdio。

qq_39451352的博客 3475

使用MATLAB和Simulink来设计并仿真一个智能家居多传感器数据融合系统

智能家居多传感器数据融合系统旨在通过集成来自多种不同类型的传感器的数据,以提供更精确和可靠的环境信息或用户状态监测。例如,将温度、湿度、光照强度、运动检测等多种传感器的数据结合起来,可以更好地了解家庭内部的状态,并做出相应的调整或响应。在这个教程中,我们将使用MATLAB和Simulink来设计并仿真一个智能家居多传感器数据融合系统。

amy_mhd的博客 189

【译】可以不要再使用Double-Checked Locking了

Double-Checked Locking方法被广泛的使用于实现多线程环境下单例模式的懒加载方式实现,不幸的是,在JAVA中,这种方式有可能不能够正常工作。在其他语言环境中,如C++,依赖于处理器的内存模型、编译器的重排序以及编译器和同步库之间的工作方式。由于这些问题在C++中并不确定,因此...

476

新系统申请软件著作权详细操作说明

今年八月底版权中心系统升级,新系统相对好用一些,至少不用再拿IE访问了,但是多了一项实名认证。下面介绍新系统的操作流程。 目录注册、登录和实名认证注册登录实名认证软件登记申请申请表填写鉴别材料身份证明文件权利归属证明文件其他材料提交材料变更登记 注册、登录和实名认证 首先访问版权中心系统:https://register.ccopyright.com.cn/login.html,如果老系统有账号直接登录即可,没有的话需要注册。 注册 点击注册按钮进入注册。 根据实际情况选择个人或单位,每个个人或单位只能在

Programming Talk 1万+

Double checked locking,两次检查的锁

最开始知道这个内容是在和室友聊天的时候发现的,它说国内某互联网公司中有一道笔试题,需要当场写出无锁队列的代码。当时对于这个概念还是一知半解,后来他说陈皓博客上有相关的内容,本着好学的精神就去一窥究竟:无锁队列的实现 基本思想是利用编译器或者语言提供的原子操作,CAS(compare and swap或者compare and set)。 bool compare-and-swap(Ad

Sands make life richful. 780

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

设计模式(十)Singleton模式和Double-Checked Locking模式

Singleton模式和Double-Checked Locking模式 Singleton模式 《设计模式》一书中的说法,Singleton模式的意图是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。 Singleton模式 关键特征 意图 希望对象只有一个实例,但是没有控制对象实例化的全局对象。还希望确保所有实体使用该对象相同的实例,而无需将引用传给它们 问题 几个...

xiaowei_innocence的博客 458

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

可以不要再使用Double-Checked Locking了

Double-Checked Locking方法被广泛的使用于实现多线程环境下单例模式的懒加载方式实现,不幸的是,在JAVA中,这种方式有可能不能够正常工作。在其他语言环境中,如C++,依赖于处理器的内存模型、编译器的重排序以及编译器和同步库之间的工作方式。由于这些问题在C++中并不确定,因此我们不能够确定具体的行为。但是在C++中显示的内存屏障是可以被用来让其正常工作的,而这些屏障在JAVA中又

横练技术 860

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

Singleton 单件模式及其变体 Double-Checked Locking 双重检查锁模式

Singleton 单件模式, 及其变体 Double-Checked Locking 双重检查锁模式, 都可以用于确保某个类只有一个对象实例化.两个模式的区别在于: Singleton 模式用于单线程程序中, 而 Double-Checked Locking 模式用于多线程应用程序. Singleton 模式的意图是: 保证一个类仅有一个实例, 并提供一个访问它的全局访问点. 工作原理: 用

qq_33364884的博客 458

c++ 单例 Double-checked Locking

Double-checked Locking 在学习C++11多线程的时候,会碰到一大堆概念,mutex, lock, atomic, memory model, memory barrier, lock-free等。要更好的理解,可以: 了解CPU的Memory Barriers机制(Paul McKenny的Memory Barriers: a Hardware V

cncnlg的专栏 2035

Double-Checked Locking is Fixed In C++11

Thedouble-checked locking pattern(DCLP) is a bit of a notorious case study inlock-free programming. Up until 2004, there was no safe way to impl...

cicangxin3389的博客 273
上一篇: 【Hibernate 面试问题】答案待续
下一篇: 移动硬盘RAW格式修复
Snoopy7713
博客等级 码龄21年 17粉丝 681原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值