【翻译】高效的double-checked线程锁

标签: 翻译 double checked | 发表时间:2014-05-07 10:44 | 作者:zhiweiofli
出处:http://www.iteye.com

      代码的性能是最重要的。然而,在当今复杂的多线程移动应用世界里,我们常常会为保证内存数据的一致性而牺牲一些性能。线程竞争条件的设计和调试是一件非常耗时,且容易令人沮丧的工作,所以线程被锁定太长时间的情况并不少见。幸运的是,现在有一些简单的模式可以使锁定变得更有效率,从而避免对性能产生不必要的影响。

      首先,让我们先预览一下只有简单 setter 代码的基类:

public class Foo {
    private Map<string, string=""> data;

    public Foo() {
        data = new HashMap<string, string="">();
    }

    public void setData(String key, String value) {
        data.put(key, value);
    }
}

       在上面的代码里,每当我们实例化一个Foo对象的同时也在实例化一个HashMap对象,而无论该HashMap是否会被使用。一般情况下,在一个强劲配置的服务器上,前面实例化的开销相对较低。但是相对一个只放在你口袋里,并且整天运行在一块电池上的设备,那样子的开销将会上升得非常快。为了提升效率,我们采用懒加载策略来重写上面的代码。

public class Foo {
    private Map<string, string=""> data;

    public Foo() { }

    public void setData(String key, String value) {
        if (data == null)
            data = new HashMap<string, string="">();

        data.put(key, value);
    }
}

       现在Foo的构造器实质上是空的构造器,而且我们只有在调用setData方法时,才会产生实例化HashMap对象的开销。在这点上,我们快速实现了只有在绝对需要的时候才去使用内存。然而这种实现的方式并非是线程安全的。线程安全是非常重要的,自从Android对线程操作的要求达到了一个根本的层面(你不可以在UI线程上执行IO阻塞的操作)。

       在上面的例子中,有两个地方需要特别注意的。第一,我们需要线程安全的数据结构。使用 ConcurrentHashMap取代HashMap可以简单地解决这点。第二,稍稍复杂一点,我们需要为属性 data 的实例化,设置更微妙的竞争条件。有这样子的可能性,两个线程同时判断属性data是否为null,并尝试为其实例化。更糟糕的是,其中一个线程会对其实例化的map置入一个对象,但该map实例将会丢失,如果两一个线程也实例化了自己的map对象。为了避免这种竞争情况,我们可以为此加上 synchronized 代码块:

public class Foo {
    private Map<string, string=""> data;

    public Foo() { }

    public void setData(String key, String value) {
        synchronized (this) {
            if (data == null)
                data = new ConcurrentHashMap<string, string="">();
        }

        data.put(key, value);
    }
}

       这就确保了同一时间里,只有一个线程进行null检查,并在需要的时候对属性data进行实例化。好像到这里,问题都迎刃而解了,然而,或许你还会记得我们的关注点是在于性能的优化,很不幸,synchronized 代码块的开销往往比较大。在这种情况下,在同一时间里,只有一个线程可以高效地访问方法setData。幸好,我们还有另一个方法可以使用:double-checked locking。在维基百科上有一篇优秀的文章对   double-checked locking 作了详尽的介绍。在我们的例子里,运用该方法后的代码如下:

public class Foo {
    private volatile Map<string, string=""> data;

    public Foo() { }

    public void setData(String key, String value) {
        if (data == null) {
            synchronized (this) {
                if (data == null)
                    data = new ConcurrentHashMap<string, string="">();
            }
        }

        data.put(key, value);
    }
}

       在上面的代码中,有两个非常重要的改变。其一,为属性data添加了volatile声明。这将指示编译器,最终是Dalvik VM,确保数据的读写操作按预读顺序(译者注: happened-before order)执行。换句话说,写数据操作,总是发生在读数据操作之前(没有这个关键字的声明,编译器或JIT优化或会使它们顺序逆转)。其次,我们在synchronized 代码块外面再添加了一层null检查。这就确保一旦属性data已被实例化,我们将不会再执行 synchronized 的代码块。然而,如果属性data确实为null,我们将 synchronize 对象,然后双重检查属性data是否为null,以确保在两次检查之间,没有其他线程没有执行属性data的实例化。如果没有这个竞争条件,代码将继续执行,继而跳出这个 synchronized 代码块。

       到这里,细心而聪明的读者或许会注意到,上述方法在Java1.5之前并不是总是可靠的。Dalvik VM也是有相似的历史,使用该方法前,请从  这里 检查一下。在这里更推荐大家阅览   这篇 描述如何在Android处理内存一致性问题的优秀指南。

 

 

        本文由 zhiweiofli编辑发布,转载请注明出处,谢谢。

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [翻译 double checked] 推荐:

【翻译】高效的double-checked线程锁

- - Java - 编程语言 - ITeye博客
      代码的性能是最重要的. 然而,在当今复杂的多线程移动应用世界里,我们常常会为保证内存数据的一致性而牺牲一些性能. 线程竞争条件的设计和调试是一件非常耗时,且容易令人沮丧的工作,所以线程被锁定太长时间的情况并不少见. 幸运的是,现在有一些简单的模式可以使锁定变得更有效率,从而避免对性能产生不必要的影响.

Double Commander : 多功能文件管理器

- qyt - Wow! Ubuntu
Double Commander 是类似于 Windows 平台上超强文件管理器 Total Commander 的替代品,提供双面板界面,跨平台,开源免费. 内置具有语法高亮的文本编辑器(F4). 内置支持 hex、二进制或文本格式的文件查看器(F3). 归档工具,支持 ZIP, TAR GZ, TGZ, LZMA , BZ2, RPM, CPIO, DEB, RAR 等格式.

Double Commander: 免費、跨平台的Total Commander

- - Integreted Life
版權所有:晴耕雨讀@ 【牧碼志】,如需轉載,請注明出處. 原文地址: http://0x3f.org/?p=2108. 由於Total Commander很貴,在Windows下又是必不可少的,我一直希望能找到一個TC的免費替代品. 試用過很多免費的文件管理器,但都不理想. 偶然看到 Double Commander,才發現這幾乎完全就是我想要的.

翻译《The rsync algorithm》

- AWard - CSDN博客推荐文章
     最近在学习Rsync工具,在对Rsync算法大加赞赏之余,决定将《The rsync algorithm 》翻译,有不正之处 还请指正. 安德鲁Tridgell 保罗马克拉斯  部计算机科学 澳大利亚国立大学 堪培拉,ACT 0200,澳大利亚.        本报告介绍了将一台计算机上的文件内容同步到另一台机器上的文件的算法(同步后保证文件内容需要一致).

闲谈翻译

- Frank - 乱象,印迹
算起来,我也算有一些翻译经验的人了,最近接连做了两次关于翻译的分享,发现对翻译有兴趣的人很多,索性,将自己关于翻译的经验做个总结,发在这里. 我是因为很偶然的机会接触翻译的. 当时还在学校,考完了TOFEL和GRE,美国对伊拉克动武,国内的报道非常奇怪,为了在论坛上争论,我开始翻译一些外国媒体的报道,发在论坛里.

翻译:WebKit for Developers

- - TaoBaoUED
Paul Irish 大湿为我们带来了这篇开年大作,文章深入浅出的阐述了各 Webkit port 的迥异,文笔细腻,是一篇不可多得的 Webkit 入门开胃菜. 为了让大家第一时间更好的品尝这道大菜,一丝特别邀请了几位 Webkit 专业开发人士作为本文的翻译顾问,在此表示由衷的感谢. 原文链接:  http://paulirish.com/2013/webkit-for-developers/.

翻译与字体

- Chenta - Apple4.us
胡天翼今天在 Twitter 上说:. 这次关于《乔布斯传》的讨论怎么都在讲翻译. 我以前从来没见过大家对一本书的翻译那么痛心疾首且富有参与精神地讨论,以至于产生了两种幻觉:1. 以前人们读的译本都很好,这次的翻译烂到让人不能相信;2. 这么多年头一次读厚书一定要抓紧机会多叫几声. 我认为这个设问的答案很明显,但不在于上述两点.

Google翻译的内涵

- hahahaha哈 - 大家都是中国人
非PS图,可以自行前往http://translate.google.com/验证.

英文笑话,带翻译

- iSingle - 河蟹娱乐
感谢河蟹网友moai的分享,来源Misc Joke,译者heather_pan,转自译言. A few days after Christmas, a mother was working in the kitchen listening to her young son playing with his new electric train in the living room.

[转载]The C10K problem翻译

- jin - 新浪开发者博客
如今的web服务器需要同时处理一万个以上的客户端了,难道不是吗. 毕竟如今的网络是个big place了. 现在的计算机也很强大了,你只需要花大概$1200就可以买一个1000MHz的处理器,2G的内存, 1000Mbit/sec的网卡的机器. 让我们来看看–20000个客户,每个为50KHz,100Kbyes和 50Kbit/sec,那么没有什么比为这两万个客户端的每个每秒从硬盘读取4千字节然后发送到网络上 去更消耗资源的了.