在Java并发编程时,同步都会存在着巨大的性能开销,因此,人们使用了很多的技巧来降低同步的影响,这其中有一些技巧很好,但是也有一些技巧存在一些缺陷,下面要结束的双重检查加锁(DCL)就是有缺陷的一类。

  由于早期的JVM在性能上存在一些有待优化的地方,因此在并发编程中,延迟初始化经常被用来降低程序的开销。编写正确的延迟初始化需要使用同步,但是直接在初始化之前使用同步会对性能产生影响。所以一些人就提出了双重检查加锁,并声称能够解决这个矛盾。如图所示就是双重检查加锁的代码。

  上图代码中,对Resource资源进行延时初始化,在初始化之前需要判断资源是否已经初始化过,所以进行了第一次没有同步的非空判断,resource对象却是为空时,再来对resource对象进行初始化,并将这部分初始化的代码加了类锁进行同步。这代码咋一看确实非常好,首先在第一次非空判断的时候,没有进行同步,只要对象完成了初始化,那么代码就永远走不到同步块中,所以不会有性能问题。但是仔细分析之后,其实是有缺陷的。

  这段代码的缺陷就是线程有可能会使用一个仅被部分构造的Resource对象。双重检查加锁的问题在于,当在没有同步的情况下读取一个共享对象的时候,最坏的情况下,有可能得到的是一个失效的值。这是由于我们在判断对象是否完成初始化是,只是判断对象的引用是否非空,然后在初始化对象时,JVM会在堆上给对象分配一块内存,然后JVM会干两件事,将内存的地址作为引用分配给变量和在堆上初始化这个对象,但是这两件事的先后顺序是不确定的,因此有可能resource非空,但是对象并没有初始化完成。

  所以在实际编程中一定要注意尽量不要使用双重检查加锁,如果一定要使用,那么可以将resource声明为volatile类型,这种方式对性能的影响是很小的,并且还能保证每个线程都能获取到最新的对象。

糟糕的双重检查加锁(DCL)的更多相关文章

  1. 单例---被废弃的DCL双重检查加锁

    被废弃的单例的DCL双重检查加锁/* *单例模式 *单例模式,保证一个类仅有一个实例,并提供一个访问它的全局访问点. *加同步锁的单例模式,适合在多线程中使用. */ class Singleton{ ...

  2. 【线程安全】—— 单例类双重检查加锁(double-checked locking)

    1. 三个版本单例类的实现 版本1:经典版 public class Singleton { public static Singleton getInstance() { if (instance ...

  3. 双重检查加锁机制(并发insert情况下数据重复插入问题的解决方案)

    双重检查加锁机制(并发insert情况下数据重复插入问题的解决方案) c#中单例模式和双重检查锁 转:https://blog.csdn.net/zhongliangtang/article/deta ...

  4. 单例模式的两种实现方式对比:DCL (double check idiom)双重检查 和 lazy initialization holder class(静态内部类)

    首先这两种方式都是延迟初始化机制,就是当要用到的时候再去初始化. 但是Effective Java书中说过:除非绝对必要,否则就不要这么做. 1. DCL (double checked lockin ...

  5. DCL,即Double Check Lock,中卫双重检查锁定。

    DCL,即Double Check Lock,中卫双重检查锁定. [Java并发编程]之十六:深入Java内存模型——happen-before规则及其对DCL的分析(含代码) 关于单例.关于DCL: ...

  6. Java并发(七):双重检验锁定DCL

    双重检查锁定(Double Check Lock,DCL) 1.懒汉式单例模式,无法保证线程安全: public class Singleton { private static Singleton ...

  7. 双重检查锁定与延迟初始化(转自infoq)

    很好的文章,转自http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization 在java程序中,有 ...

  8. Java中的双重检查锁(double checked locking)

    最初的代码 在最近的项目中,写出了这样的一段代码 private static SomeClass instance; public SomeClass getInstance() { if (nul ...

  9. volatile双重检查锁定与延迟初始化

    一.基本概念: 1.volatile是轻量级的synchronized,在多核处理器开发中保证了共享变量的“可见性”.可见性的意思是,当一个线程修改一个共享变量时,另一个线程能读到这个修改的值. 2. ...

随机推荐

  1. datatables使用总结篇

    <!doctype html> <html> <head> <meta charset="gbk"/> <meta name= ...

  2. linux中find命令的使用

    google找到一个很好的讲解:http://www.chinaz.com/server/2009/0807/85796.shtml

  3. hdu 3496 Watch The Movie

    题意:题目给定N部电影,每部电影有时长和价值,要求看M部电影,并且时间控制在L以内,转化为背包问题,让我们在N件物品中找正好M件物品塞进容量L的包中,求最大的价值.// dp[i][j] 表示在容量为 ...

  4. 安装服务Memcached+Nginx+Php linux下安装

    Memcached安装 1.      源码安装libevent(下载地址:http://monkey.org/~provos/libevent/) 2.      源码安装memcached(下载地 ...

  5. Java Error和Exception区别

    Error和Exception都继承自Throwable: 二者不同之处: Exception: 1.可以是可被控制(checked)或者不可控制(unchecked): 2.表示一个由程序员导致的错 ...

  6. JS触发ASP.NET服务器端控件的方法

    <asp:Button ID="Button_regId" runat="server" Font-Bold="False" OnCl ...

  7. 联通光纤上网配置+华为HG8240光猫+TL-WR842N

    最近搬家改用北京联通宽带,光纤入户的那种.联通送的光猫是华为HG8240,没看到天线,应该是不带无线路由.然后自己再买了个TP-Link的TL-WR842N,用来组局域网,也供ipad.kindle. ...

  8. codeforces 260 div2 C题

    C题就是个dp,把原数据排序去重之后得到新序列,设dp[i]表示在前i个数中取得最大分数,那么: if(a[i] != a[i-1]+1)   dp[i] = cnt[a[i]]*a[i] + dp[ ...

  9. C ~ 一个串口接收思路

    void uart_rx_isr(void) //接收中断函数 { uchar c; c=SBUF;//c等于接收的字节: switch (recv_state) { : if (c==0x02) / ...

  10. 【windows核心编程】使用远程线程注入DLL

    前言 该技术是指通过在[目标进程]中创建一个[远程线程]来达到注入的目的. 创建的[远程线程]函数为LoadLibrary, 线程函数的参数为DLL名字, 想要做的工作在DLL中编写.  示意图如下: ...