`

双检锁DLC(转)

    博客分类:
  • JVM
阅读更多
对于双检锁,其实有多种不同的用法,有很多种用法是无论如何不会出现问题的.
我最初用双检锁来获取jndi对象时,立即有人告诉我双检锁是不安全的,我笑着告诉他:是否安全
我比你更有把握.

static DataSource ds = null;

public static DataSource getDataSource(){
if(ds == null){
  synchronized(XXX.class){
   if(ds == null)
    ds = xxx;
  }
}

return ds;
}

这样的DCL有什么安全问题呢?它仅仅是为了不做重复的劳动.一是ds本身是已经存在的对象,不是动态
构造的,二是即使多次获取它也还是同一引用.这里做的工作仅仅是不想让另一个线程多做在JNDI上再查
找一次的工作,因为查找本身是耗时的,与其让另一个线程再查找还不如把它阻塞在synchronized外面什
么也不做.事实上即使是再查找一次两次获得的还是相同的引用,而且是已经构造好,不存在初始化问题
的对象.DCL会有什么安全问题?

所以不要一看到双检锁就认为它有安全问题.有些时候它完全可以非常好地工作.只是你要理解它的安全
问题到底在哪儿?


DCL到底有什么问题?

DCL的安全性最初被公开的时候在2001年JavaWorld.其实那个例子在那个时候已经是错误的.影响了后面几
代人都跟着错误.有时候我甚至不敢相信一些非常简单的问题因为放在权威的地方就没有人敢去责疑.

最初的问题提出者在这儿:http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html
例子是:
class SomeClass {
  private Resource resource = null;

  public Resource getResource() {
    if (resource == null) {
      synchronized {
        if (resource == null)
          resource = new Resource();
      }
    }
    return resource;
  }
}

我不知道提这个问题的人是否是个老古董.在2001年,JVM1.2已经发布好久了.JMM(Java Memory Model)已经
已经发布了新的规范,竟然在这个时候提出这样的问题.而且这个已经不存在的问题在此后的五年(今年是2006年)
中一误导着很多人,甚至是一些博士.

这个问题的提出者是这样说的.当一个线程运行到resource = new Resource();时,因为new一个对象需要分配空
间,初始化字段,调用构造方法.当为resource分配好空间后,外面的其它线程就可以看到不为null的resource,而这
时有可能还没有初始化字段和调用构造方法就被其它线程引用了.

确实,在JAVA2(以jdk1.2开始)以前对于实例字段是直接在主储区读写的.所以当一个线程对resource进行分配空间,
初始化和调用构造方法时,可能在其它线程中分配空间动作可见了,而初始化和调用构造方法还没有完成.

但是从JAVA2以后,JMM发生了根本的改变,分配空间,初始化,调用构造方法只会在线程的工作存储区完成,在没有
向主存储区复制赋值时,其它线程绝对不可能见到这个过程.而这个字段复制到主存区的过程,更不会有分配空间后
没有初始化或没有调用构造方法的可能.在JAVA中,一切都是按引用的值复制的.向主存储区同步其实就是把线程工作
存储区的这个已经构造好的对象有压缩堆地址值COPY给主存储区的那个变量.这个过程对于其它线程,要么是resource
为null,要么是完整的对象.绝对不会把一个已经分配空间却没有构造好的对象让其它线程可见.

至于不同线程如何看到另一个线程对实例字段的改变,可以参看我的另一篇文章:深入理解JMM.

那么上面的例子是否有问题?
我可以负责地说,在JVM1.2以后,上面的例子没有问题.(也许还是有问题但以我的水平还没有发现,但绝对不是JW上提
出的那样的问题,那个问题只在1.2以前出现,而2001年早已不是jdk1.1的时代了),很多时候我们需要有独立的思考,
当你觉得你获取一个新知识点时你一定要弄清它存在的环境.

那么到底DCL是否真的就没有问题了呢?
否,现在的问题还是可见性问题,但问题转到了同一对象不同字段上面,这个问题已经在以前说过了:
public MyObject{
    private static MyObect obj;
    private Date d = new Data();
    public Data getD(){return this.d;}
    public static MyObect  getInstance(){
        if(obj == null){
            synchronized(MyObect .class){
                if(obj == null)
                    obj = new MyObject();//这里
            }
        }
        return obj;
    }
}
一个线程A运行到"这里"时,对于A的工作区中,肯定已经产生一个MyObect对象,而且这时这个对象已经
完成了Data d.现在线程A调用时间到,执行权被切换到另一个线程B来执行,会有什么问题呢?

如果obj不为null,线程B获得了一个obj,但可能obj.getD()却还没有初始化.

为什么?既然obj已经可见了(线程A还没有离开同步块),而d却不可见呢?
如果d不可见,那么obj也应该为null啊?线程B应该等待A释放同步块啊?

事实上,对于"这里"这条语句,线程A还没有离开同步块.
因为没有"离开同步块"这个条件,线程a的工作区没有强制与主存储器同步,这时工作区中有两个字段
obj,d 到底先把谁同步到主存储区,没有条件限制,虽然在线程A的工作区obj和d都是完整的,但有JSL
没有强制不允许先把obj映射到主存储区,如果哪个jvm实现按它的优化方案先把工作存储器中的obj
同步到主存储器了,这时正好线程B获取了,而d却没有同步过去,那么线程B就获取了obj的引用却找不能
obj.getD();


我们发现,其实对于安全性问题都是基于即时构造对象这样的条件下的.如果你把DCL用来控制其它不重
复操作,它就不会出现这样的问题,就象上面从JNDI中查找DataSource,因为查找本身是耗时的,所以我用
DCL来控制"查找"这个行为而不是控制对象本身.其实还有很多可以正确应用DCL的地方.象JLive,OFBiz
这些开源项目中都在大量应用DCL,当然我不是推荐大家都去用DCL,而是如果需要而且合适就去用.有些
问题可以用其它方法来代替.只要你能真正明白它在什么时候是不安全的,你就可以安全地使用DCL
分享到:
评论

相关推荐

    [武装突袭 3 ] DLC一键解锁工具 [12.10更新]_dlc解锁_arma3_

    在提供的压缩包文件中,有两个文件: "[武装突袭 3 ] DLC一键解锁工具 [12.10更新]" 和 "[╬Σ╫░═╗╧« 3 ] DLC╥╗╝ⁿ╜Γ╦°╣ñ╛▀ [12.10╕ⁿ╨┬]",它们可能是两个版本或者备份的DLC解锁工具。...

    城市天际线2 全DLC解锁

    城市天际线2 全DLC解锁 都市 天际线 2 DLC 解锁补丁 [Steam] [Windows & SteamOS] DLC 解锁列表(仅供参考) 《都市 天际线 2》 - 旧金山(预购) 《都市 天际线 2》 - 地标建筑(预购) 《都市 天际线 2》 - 海滩...

    胡闹厨房2 DLC扩展包

    胡闹厨房2 DLC,一键解锁,支持创意工坊,成就,云存档,在线游玩,多人游玩只需一人拥有即可。

    rockman9dlc_洛克人9美版_rockman9dlc_

    标题中的"rockman9dlc_洛克人9美版_rockman9dlc_"表明我们关注的是游戏的下载内容(DLC,Downloadable Content),特别提到了“美版”,意味着这是面向北美市场的版本。描述中的“洛克人9日版DLC,可以选择布鲁斯...

    Frostpunk-steam版-全DLC解锁

    "全DLC解锁"意味着玩家可以获得游戏的所有扩展内容,提升游戏体验。以下是各个DLC的主要内容: 1. **冬日之家陨落(The Fall of Winterhome)**:这是游戏的第一个故事DLC,引入了新的剧情章节。玩家将面临一个被...

    DLC.rar_DLC_DLC 调速_双闭环 可逆_环流调速_直流调速

    在"DLC.rar"压缩包内的"DLC.mdl"文件,很可能是MATLAB或Simulink模型,用于模拟和分析这种逻辑无环流双闭环可逆直流调速系统。这样的模型可以帮助工程师理解和设计这类系统,通过仿真验证控制策略的有效性,并进行...

    DLC300 驱动

    DLC300驱动是专为DLC300-L系列工业摄像头设计的一款重要软件组件,主要用于确保摄像头能与计算机系统正确、高效地通信。在工业应用中,高分辨率、稳定性和实时性能是摄像头的关键特性,DLC300驱动正是为了满足这些...

    DLC协议 win xp win 2003

    在Windows XP及后续版本中,微软不再支持DLC协议,转而推荐使用更现代的协议,如TCP/IP协议栈,以实现更高效、更安全的网络连接。然而,有些特定的场景或用户可能仍需要在较新的系统如Windows Server 2003中使用RPL...

    Access DLC表格篇教程,适合Access DLC表格学习使用

    在Access数据库中,DLC(Database Linking and Compact)表格是一种重要的数据管理工具,它允许用户连接到外部数据源,如Excel、SQL Server或其他Access数据库。本教程将深入讲解Access DLC表格的相关操作,包括链接...

    ism330dlc_STdC.zip

    【ism330dlc驱动加demo】是一个针对ISM330DLC传感器的软件开发资源包,主要用于嵌入式系统。这篇详细说明将深入探讨ISM330DLC传感器的特点、驱动程序的工作原理以及如何在嵌入式环境中集成和应用。 ISM330DLC是意法...

    论文研究 - 多层DLC / BN膜的沉积和表征

    在本文中,报道了对多层类金刚石碳和氮化硼(DLC / BN)膜进行研究获得的结果。 使用透射电子显微镜,螺旋钻电子显微镜,纳米压痕测量和圆盘摩擦试验表征了薄膜的微观结构,原子浓度,硬度和摩擦系数。 研究了双层...

    基于DLC的空调负荷双层优化调度和控制策略

    空调负荷占电力终端设备的比重逐年增长,已成为峰 荷最重要的组成部分,加深了中国高峰时段的供需矛盾,导 致峰谷差进一步拉大。但同时空调负荷具有一定热存储能 力,具备巨大的负荷调节潜力,可以通过需求响应纳入...

    DLC类金刚石薄膜技术和工艺

    ### DLC类金刚石薄膜技术和工艺 #### 一、引言 类金刚石碳膜(Diamond-Like Carbon,简称DLC)是一种具有优异物理特性的薄膜材料,在多种领域展现出广泛的应用前景。DLC薄膜通常表现出高硬度、低摩擦系数、良好的...

    Access DLC字符串篇教程

    在Access DLC字符串篇教程中,我们探讨了与字符串操作相关的多个关键知识点,这些知识点对于处理和分析文本数据至关重要。以下是对这些知识点的详细说明: 1. **字符串字节数的计算**: 在VBScript中,`LenB`函数...

    sak32009-get-dlc-info-from-steamdb.user.js_sak32009-get-dlc_stea

    标题 "sak32009-get-dlc-info-from-steamdb.user.js" 和描述 "sak32009-get-dlc-info-from-steamdb.user" 提供的信息表明,这是一个与Steam游戏平台相关的用户脚本,可能用于获取游戏的DLC(Downloadable Content)...

    金橙子DLC2-PCIE-QCW5V板卡用户手册

    - **通用IO口与飞行标刻接口J17 (DC3-40转DB37)**:提供了多种输入输出接口,用于连接开关、脚踏开关、继电器和编码器等。 - **激光器接口J6 (DB25)**:这是连接激光器的接口,其中PIN15的定义在不同的版本中有...

    氮气流量对AEGD混合CVD涂层含铬DLC(金刚石碳)涂层的影响

    随着对电极材料的小型化的兴趣的增长,电动和混合动力汽车的... 同时输入氮气会导致DLC涂层中Cr和N按特定比例沉积CrN,并且氮原子取代键中的氢以增加DLC膜中未沉积CrN的sp3键结构。导致特定电阻率等于或小于特定值。

    Koalageddon:适用于Steam,Epic,Origin和Uplay(R1)的合法DLC解锁器

    适用于Steam,Epic,Origin和Uplay(R1)的合法DLC解锁器 欢迎使用DreamAPI存储库。 有关用户友好的介绍或支持,请查看。 本文档适用于软件开发人员。 :clamp: 解决方案项目 :toolbox: 常见的 该项目是一个静态库...

    艾尔登法环-豪华中文-V1.12.1+黄金树幽影DLC-黑焰刀魂-魅影断弦+预购特典+全DLC-解压即撸

    艾尔登法环|豪华中文|V1.12.1+黄金树幽影DLC-黑焰刀魂-魅影断弦+预购特典+全DLC|解压即撸|

    yolo dlcyolo dlc

    yolo dlcyolo dlcyolo dlcyolo dlcyolo dlcyolo dlcyolo dlcyolo dlcyolo dlcyolo dlcyolo dlc

Global site tag (gtag.js) - Google Analytics