Java中可怕的双重检查锁定成语

本文讨论的问题不是新问题,但即使是经验丰富的开发人员也仍然很棘手。 单例模式是常见的编程习惯。 但是,当与多个线程一起使用时,必须进行某种类型的同步,以免破坏代码。

Khangaonkar报告中的 JCG合作伙伴Manoj Khangaonkar在一篇相关文章中详细研究了双重检查的成语,以了解其崩溃之处,并提出了所有可能的解决方案:

免得他说些什么:

Java中的双重检查锁定问题已得到充分证明。 但是,即使是经验丰富的程序员也可能会过分热衷于尝试优化代码同步,从而创建单例并使其陷入陷阱。

考虑代码

public class Sample {
  private static Sample s = null ;
  public static Sample getSample() {
    if (s == null) {
      s = new Sample() ;
    }
  return s ;
  }
}
清单1

此代码不是线程安全的。 如果2个线程t1和t2同时进入getSample()方法,则它们很可能会获得不同的样本实例。 通过将synced关键字添加到getSample()方法,可以轻松解决此问题。

public class Sample {
  private static Sample s = null ;
  public static synchronized Sample getSample() {
    if (s == null) {
      s = new Sample() ;
    }
    return s ;
  }
}
清单2

现在,getSample方法可以正常工作。 在进入getSample方法之前,线程t1获得一个锁。 需要进入该方法的任何其他线程t2将阻塞,直到t1退出该方法并释放锁为止。 代码有效。 生活很好。 这是精明的程序员在不谨慎的情况下可以超越自己的地方。 他将注意到,实际上,仅对创建实例的getSample的第一次调用需要进行同步,而仅返回s的后续调用将付出不必要的代价。 他决定优化代码以

public class Sample {
  private static Sample s = null ;
  public static Sample getSample() {
    if (s == null) {
      synchronized(Sample.class) {
        s = new Sample() ;
      }
    }
    return s ;
  }
}
清单3

我们的Java专家很快意识到该代码与清单1所存在的问题相同。 因此,他进一步对其进行了微调。

public class Sample {
  private static Sample s = null ;
  public static Sample getSample() {
    if (s == null) {
      synchronized(Sample.class) {
        if (s == null) {
          s = new Sample() ;
        }
      }
    }
    return s ;
  }
}
清单4

通过在同步块中添加额外的检查,他确保了只有一个线程将创建该示例的实例。 这是双重检查模式。 我们的老师的朋友,一位Java专家,好友检查了该代码。 签入代码并发货。 生活好吗?

! 假设线程t1进入getSample。 s为空。 它锁了。 在同步块内,它检查s是否仍然为null,然后执行Sample的构造函数。 在构造函数执行完成之前,将t1换出,并且t2得到控制。 由于构造函数未完成,因此s被部分初始化。 它不为null,但具有一些损坏或不完整的值。 当t2进入getSample时,它将看到s不为null并返回一个损坏的值。

总之,仔细检查模式不起作用。 选项是在清单2所示的方法级别进行同步,或者放弃同步并使用一个静态字段,如下所示。

public class Sample {
  private static Sample INSTANCE = new Sample();

  public static Sample getSample()  {
    return INSTANCE ;
  }
}
清单5

好是好人的敌人!

拜伦

相关文章:


翻译自: https://www.javacodegeeks.com/2011/03/dreaded-double-checked-locking-idiom-in.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值