**双重检查锁定模式**（也被称为"双重检查加锁优化"，"锁暗示"（Lock hint）) 是一种软件设计模式用来减少并发系统中竞争和同步的开销。双重检查锁定模式首先验证锁定条件(第一次检查)，只有通过锁定条件验证才真正的进行加锁逻辑并再次验证条件(第二次检查)。

该模式在某些语言在某些硬件平台的实现可能是不安全的。有的时候，这一模式被看做是反模式。

它通常用于减少加锁开销，尤其是为多线程环境中的单例模式实现"惰性初始化"。惰性初始化的意思是直到第一次访问时才初始化它的值。

Java中的使用
--------

考虑下面的Java代码（页面存档备份，存于）

```
// Single threaded version
class Foo {
 private Helper helper = null;
 public Helper getHelper() {
 if (helper == null) {
 helper = new Helper();
 }
 return helper;
 }

 // other functions and members...
}

```

这段在使用多线程的情况下无法正常工作。在多个线程同时调用`getHelper()`时，必须要获取锁，否则，这些线程可能同时去创建对象，或者某个线程会得到一个未完全初始化的对象。

锁可以通过代价很高的同步来获得，就像下面的例子一样。

```
// Correct but possibly expensive multithreaded version
class Foo {
 private Helper helper = null;
 public synchronized Helper getHelper() {
 if (helper == null) {
 helper = new Helper();
 }
 return helper;
 }

 // other functions and members...
}

```

只有`getHelper()`的第一次调用需要同步创建对象，创建之后`getHelper()`只是简单的返回成员变量，而这里是无需同步的。
由于同步一个方法会降低100倍或更高的性能， 每次调用获取和释放锁的开销似乎是可以避免的：一旦初始化完成，获取和释放锁就显得很不必要。许多程序员以下面这种方式进行优化：

1. 检查变量是否被初始化(不去获得锁)，如果已被初始化立即返回这个变量。
2. 获取锁
3. 第二次检查变量是否已经被初始化：如果其他线程曾获取过锁，那么变量已被初始化，返回初始化的变量。
4. 否则，初始化并返回变量。

```
// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
 private Helper helper = null;
 public Helper getHelper() {
 if (helper == null) {
 synchronized(this) {
 if (helper == null) {
 helper = new Helper();
 }
 }
 }
 return helper;
 }

 // other functions and members...
}

```

直觉上，这个算法看起来像是该问题的有效解决方案。然而，这一技术还有许多需要避免的细微问题。例如，考虑下面的事件序列：

1. 线程*A*发现变量没有被初始化, 然后它获取锁并开始变量的初始化。
2. 由于某些编程语言的语义，编译器生成的代码允许在线程*A*执行完变量的初始化之前，更新变量并将其指向部分初始化的对象。
3. 线程*B*发现共享变量已经被初始化，并返回变量。由于线程*B*确信变量已被初始化，它没有获取锁。如果在*A*完成初始化之前共享变量对*B*可见（这是由于*A*没有完成初始化或者因为一些初始化的值还没有覆盖*B*使用的内存(缓存一致性)），程序很可能会崩溃。

  

在J2SE 1.4或更早的版本中使用双重检查锁有潜在的危险，有时会正常工作：区分正确实现和有小问题的实现是很困难的。取决于编译器，线程的调度和其他并发系统活动，不正确的实现双重检查锁导致的异常结果可能会间歇性出现。重现异常是十分困难的。

在J2SE 5.0中，这一问题被修正了。volatile关键字保证多个线程可以正确处理单件实例。（页面存档备份，存于）描述了这一新的语言特性:

```
// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
 private volatile Helper helper = null;
 public Helper getHelper() {
 Helper result = helper;
 if (result == null) {
 synchronized(this) {
 result = helper;
 if (result == null) {
 helper = result = new Helper();
 }
 }
 }
 return result;
 }

 // other functions and members...
}

```

注意局部变量result的使用看起来是不必要的。对于某些版本的Java虚拟机，这会使代码提速25%，而对其他的版本则无关痛痒。

如果helper对象是静态的(每个类只有一个), 可以使用双重检查锁的替代模式惰性初始化模式。查看 上的列表16.6。

```
// Correct lazy initialization in Java
@ThreadSafe
class Foo {
 private static class HelperHolder {
 public static Helper helper = new Helper();
 }

 public static Helper getHelper() {
 return HelperHolder.helper;
 }
}

```

这是因为内部类直到他们被引用时才会加载。

Java 5中的final语义可以不使用volatile关键字实现安全的创建对象：

```
public class FinalWrapper<T> {
 public final T value;
 public FinalWrapper(T value) {
 this.value = value;
 }
}

public class Foo {
 private FinalWrapper<Helper> helperWrapper = null;

 public Helper getHelper() {
 FinalWrapper<Helper> wrapper = helperWrapper;

 if (wrapper == null) {
 synchronized(this) {
 if (helperWrapper == null) {
 helperWrapper = new FinalWrapper<Helper>(new Helper());
 }
 wrapper = helperWrapper;
 }
 }
 return wrapper.value;
 }
}

```

为了正确性，局部变量wrapper是必须的。这一实现的性能不一定比使用volatile的性能更高。

Microsoft Visual C++ 中的使用
-------------------------

如果指针是由C++关键字**volatile**定义的，那么双重检查锁可以在Visual C++ 2005 或更高版本中实现。Visual C++ 2005 保证volatile变量是一种内存屏障，阻止编译器和CPU重新安排读入和写出语义。 在先前版本的Visual C++则没有此类保证。在其他方面将指针定义为volatile可能会影响程序的性能。例如，如果指针定义对代码的其他地方可见，强制编译器将指针视为屏障，就会降低程序的性能，这是完全不必要的。

参见
--

* 测试和测试并设置idiom作为底层加锁机制。
* 按需初始化持有者作为Java中线程安全的替代者。

参考资料
----

1. ↑  Schmidt, D et al. Pattern-Oriented Software Architecture Vol 2, 2000 pp353-363
2. ↑  Boehm, Hans-J. "Threads Cannot Be Implemented As a Library", ACM 2005, p265
3. ↑  Joshua Bloch "Effective Java, Second Edition", p. 283
4. ↑  Brian Goetz et al. Java Concurrency in Practice, 2006 pp348
5. ↑ .  [2012-02-12]. （原始内容存档于2012-03-03）.
6. ↑   （页面存档备份，存于） Javamemorymodel-discussion mailing list
7. ↑ .  [2012-02-12]. （原始内容存档于2012-10-20）.

外部链接
----

* Issues with the double checked locking mechanism captured in Jeu George's Blogs Pure Virtuals
* Implementation of Various Singleton Patterns including the Double Checked Locking Mechanism（页面存档备份，存于） at TEKPOOL（页面存档备份，存于）
* "Double Checked Locking" Description from the Portland Pattern Repository
* "Double Checked Locking is Broken" Description from the Portland Pattern Repository
* Paper "C++ and the Perils of Double-Checked Locking（页面存档备份，存于）" (475 KB) by Scott Meyers and Andrei Alexandrescu
* Article "Double-checked locking: Clever, but broken" by Brian Goetz
* The "Double-Checked Locking is Broken" Declaration（页面存档备份，存于）; David Bacon et al.
* Double-checked locking and the Singleton pattern（页面存档备份，存于）
* Singleton Pattern and Thread Safety
* volatile keyword in VC++ 2005（页面存档备份，存于）
* Java Examples and timing of double check locking solutions

  


|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
软件设计模式 |
| --- |
|  |
| 设计模式 | 

|  |  |
| --- | --- |
| 创建型 | 
* 抽象工厂
* 生成器
* 工厂方法
* 惰性初始
* 原型
* 单例

 |
|  |
| 结构型 | 
* 适配器
* 桥接
* Composite（英语：）
* 修饰
* 外观
* 享元
* 代理

 |
|  |
| 行为型 | 
* 责任链
* 命令
* Interpreter（英语：）
* 迭代器
* 中介者
* Memento（英语：）
* 观察者
* Specification（英语：）
* State（英语：）
* 策略
* 模板方法
* 访问者

 |

 |
|  |
| 并行模式 | 
* 主动对象（英语：）
* 阻止（英语：）
* Binding properties（英语：）
* 双重检查锁定模式
* 异步方法调用
* Future与promise
* Guarded suspension（英语：）
* Join（英语：）
* 锁
* Messaging（英语：）
* 监视器
* Proactor（英语：）
* 反应器
* 读写锁
* 调度
* 线程池
* 线程局部存储

 |
|  |
| 架构模式 | 
* ADR（英语：）
* 主动记录
* Broker（英语：）
* 主從式架構
* cbd
* DAO
* DDD
* Data transfer object（英语：）
* Front controller（英语：）
* Identity map（英语：）
* Interceptor（英语：）
* 控制反转
* MVC
* 微服務
* MVP
* 單體式應用程式
* 多层架构
* *n*-tier（英语：）
* Naked objects（英语：）
* P2P
* 发布/订阅
* REST
* SOA
* Specification（英语：）

 |
|  |
| 云分布式 | 
* 断路器
* CQRS（英语：）
* 补偿交易（英语：）
* 索引表
* 领导者选举（英语：）
* MapReduce
* 物化视图（英语：）
* 管道
* 过滤器（英语：）
* 发布/订阅
* 分片（英语：）
* 节流（英语：）

 |
|  |
| 其他模式 | 
* Blackboard（英语：）
* Business delegate（英语：）
* Composite entity（英语：）
* 委托
* 依赖注入
* Intercepting filter（英语：）
* 惰性載入
* Method chaining（英语：）
* 模拟对象
* 空对象
* 对象池
* Servant（英语：）
* Twin（英语：）
* Type tunnel（英语：）

 |
|  |
| 书籍 | 
* 设计模式：可复用面向对象软件的基础
* 企业集成模式（英语：）

 |
|  |
| 人物 | 
* 克里斯托佛·亚历山大
* 埃里希·伽瑪
* Ralph Johnson（英语：）
* John Vlissides（英语：）
* 葛來迪·布區
* 肯特·貝克
* 沃德·坎宁安
* 马丁·福勒
* Robert Martin（英语：）
* Jim Coplien（英语：）
* Douglas Schmidt（英语：）
* Linda Rising

 |
|  |
| 社区 | 
* The Hillside Group（英语：）
* The Portland Pattern Repository（英语：）

 |

 |