首页 > 其他 > 详细

DCL双检锁的失效:现实与初衷的背离

时间:2018-01-04 00:03:08      阅读:305      评论:0      收藏:0      [点我收藏+]

最近看了Brian Goetz写的一篇有关DCL的文章:Double-checked locking: Clever, but broken。( 2001年发表于JavaWorld上)

这篇文章讲述了DCL设计的初衷,但是因为JVM的不同实现(没有严格遵循JMM规范)导致DCL在实际应用中失效。

 

1. DCL的设计初衷

DCL是为了支持 Lazy initialization而设计的。

我们有多种方式去实现单例模式:

  • Eager initialization: 饿加载,当类加载器加载类时就初始化类变量。这种方式可以保证初始化的原子性,但降低了程序的启动性能(类加载过程中的初始化阶段对类变量初始化)。
  • Lazy initialization: 懒加载,当需要使用类变量时才进行初始化。这种方式可以提高程序的启动性能,但需要同步来保证初始化的原子性,无疑带来了同步开销。

DCL的目标是(1)提高程序的启动性能,(2)降低同步开销。

代码示例:

1 class SomeClass {
2   private Resource resource = null;
3   public Resource getResource() {
4     if (resource == null)
5       resource = new Resource();
6     return resource;
7   }
8 }

 

2. DCL失效的原因

DCL要真正有效,需要依赖JVM真正遵循了JMM规范。

实际中,编译器、处理器、缓存都可以“自由”执行代码(乱序执行),只要保证as-if-serial semantics就行。

这里要提及synchronized的作用:

  • 互斥执行某段代码
  • 触发memory barrier,强制线程的工作内存与住内存同步

适当地使用同步可以保证:一个线程预期地看到另一个线程的影响(The proper synchronization guarantees that one thread will see the effects of another in a predictable manner)。

DCL之所以失效,是因为没有同步地使用resource。(与上文提到的乱序执行有关,这个与具体的JVM版本有关,很多JVM实现没有严格实现JMM规范)

 

3. 解决方案

(1)不使用DCL;

(2)使用饿加载(Eager initialization),由类加载器保证类变量初始化的互斥性(每个类只会加载一次);

(3)懒加载有个特例:只含有一个类变量,不含类方法、实例变量、实例方法;

(4)对 32bit primitive values 有效。

 

DCL双检锁的失效:现实与初衷的背离

原文:https://www.cnblogs.com/huangzejun/p/8185220.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!