- 浏览: 198345 次
- 性别:
- 来自: 上海
文章分类
最新评论
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
To first explain the desired behavior, consider the following code:
If this code was used in a multithreaded context, many things could go wrong. Most obviously, two or more Helper objects could be allocated. (We'll bring up other problems later). The fix to this is simply to synchronize the getHelper() method:
The code above performs synchronization every time getHelper() is called. The double-checked locking idiom tries to avoid synchronization after the helper is allocated:
It doesn't work
There are lots of reasons it doesn't work. The first couple of reasons we'll describe are more obvious. After understanding those, you may be tempted to try to devise a way to "fix" the double-checked locking idiom. Your fixes will not work: there are more subtle reasons why your fix won't work. Understand those reasons, come up with a better fix, and it still won't work, because there are even more subtle reasons.
Lots of very smart people have spent lots of time looking at this. There is no way to make it work without requiring each thread that accesses the helper object to perform synchronization.
The first reason it doesn't work
The most obvious reason it doesn't work it that the writes that initialize the Helper object and the write to the helper field can be done or perceived out of order. Thus, a thread which invokes getHelper() could see a non-null reference to a helper object, but see the default values for fields of the helper object, rather than the values set in the constructor.
If the compiler inlines the call to the constructor, then the writes that initialize the object and the write to the helper field can be freely reordered if the compiler can prove that the constructor cannot throw an exception or perform synchronization.
Even if the compiler does not reorder those writes, on a multiprocessor the processor or the memory system may reorder those writes, as perceived by a thread running on another processor.
Doug Lea has written a more detailed description of compiler-based reorderings.
http://gee.cs.oswego.edu/dl/cpj/jmm.html
A fix that doesn't work
Given the explanation above, a number of people have suggested the following code:
Fixing Double-Checked Locking using Thread Local Storage
Alexander Terekhov (TEREKHOV@de.ibm.com) came up clever suggestion for implementing double checked locking using thread local storage. Each thread keeps a thread local flag to determine whether that thread has done the required synchronization.
Fixing Double-Checked Locking using Volatile
JDK5 and later extends the semantics for volatile so that the system will not allow a write of a volatile to be reordered with respect to any previous read or write, and a read of a volatile cannot be reordered with respect to any following read or write. See this entry in Jeremy Manson's blog for more details.
// 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...
}
Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others,
If the helper object is static (one per class loader), an alternative is the initialization on demand holder idiom
// 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;
}
}
This relies on the fact that inner classes are not loaded until they are referenced.
Semantics of final field in Java 5 can be employed to safely publish the helper object without using volatile:[7]
To first explain the desired behavior, consider the following code:
// Single threaded version class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) helper = new Helper(); return helper; } // other functions and members... }
If this code was used in a multithreaded context, many things could go wrong. Most obviously, two or more Helper objects could be allocated. (We'll bring up other problems later). The fix to this is simply to synchronize the getHelper() method:
// Correct multithreaded version class Foo { private Helper helper = null; public synchronized Helper getHelper() { if (helper == null) helper = new Helper(); return helper; } // other functions and members... }
The code above performs synchronization every time getHelper() is called. The double-checked locking idiom tries to avoid synchronization after the helper is allocated:
// 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... }
It doesn't work
There are lots of reasons it doesn't work. The first couple of reasons we'll describe are more obvious. After understanding those, you may be tempted to try to devise a way to "fix" the double-checked locking idiom. Your fixes will not work: there are more subtle reasons why your fix won't work. Understand those reasons, come up with a better fix, and it still won't work, because there are even more subtle reasons.
Lots of very smart people have spent lots of time looking at this. There is no way to make it work without requiring each thread that accesses the helper object to perform synchronization.
The first reason it doesn't work
The most obvious reason it doesn't work it that the writes that initialize the Helper object and the write to the helper field can be done or perceived out of order. Thus, a thread which invokes getHelper() could see a non-null reference to a helper object, but see the default values for fields of the helper object, rather than the values set in the constructor.
If the compiler inlines the call to the constructor, then the writes that initialize the object and the write to the helper field can be freely reordered if the compiler can prove that the constructor cannot throw an exception or perform synchronization.
Even if the compiler does not reorder those writes, on a multiprocessor the processor or the memory system may reorder those writes, as perceived by a thread running on another processor.
Doug Lea has written a more detailed description of compiler-based reorderings.
http://gee.cs.oswego.edu/dl/cpj/jmm.html
A fix that doesn't work
Given the explanation above, a number of people have suggested the following code:
// (Still) Broken multithreaded version // "Double-Checked Locking" idiom class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { Helper h; synchronized(this) { h = helper; if (h == null) synchronized (this) { h = new Helper(); } // release inner synchronization lock helper = h; } } return helper; } // other functions and members... }
Fixing Double-Checked Locking using Thread Local Storage
Alexander Terekhov (TEREKHOV@de.ibm.com) came up clever suggestion for implementing double checked locking using thread local storage. Each thread keeps a thread local flag to determine whether that thread has done the required synchronization.
class Foo { /** If perThreadInstance.get() returns a non-null value, this thread has done synchronization needed to see initialization of helper */ private final ThreadLocal perThreadInstance = new ThreadLocal(); private Helper helper = null; public Helper getHelper() { if (perThreadInstance.get() == null) createHelper(); return helper; } private final void createHelper() { synchronized(this) { if (helper == null) helper = new Helper(); } // Any non-null value would do as the argument here perThreadInstance.set(perThreadInstance); } }
Fixing Double-Checked Locking using Volatile
JDK5 and later extends the semantics for volatile so that the system will not allow a write of a volatile to be reordered with respect to any previous read or write, and a read of a volatile cannot be reordered with respect to any following read or write. See this entry in Jeremy Manson's blog for more details.
// Works with acquire/release semantics for volatile // Broken under current semantics for volatile class Foo { private volatile Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) helper = new Helper(); } } return helper; } }
// 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...
}
Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others,
If the helper object is static (one per class loader), an alternative is the initialization on demand holder idiom
// 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;
}
}
This relies on the fact that inner classes are not loaded until they are referenced.
Semantics of final field in Java 5 can be employed to safely publish the helper object without using volatile:[7]
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; } }
发表评论
-
Java 5 并发学习(转)
2012-06-26 14:38 863Java 5 并发学习 在Java5之后,并发线程这块发生 ... -
WeakHashMap和HashMap的区别
2012-02-24 13:42 845http://mzlly999.iteye.com/blog/ ... -
volatile 的高级模式
2012-02-22 13:07 736前面几节介绍的模式涵盖了大部分的基本用例,在这些模式中使用 v ... -
Log4j配置文件详细说明[转]
2012-02-14 10:48 1346属性文件Properties properties属性文件 ... -
Log4j的配置文件
2012-02-14 10:42 817Log4j支持两种配置文件格式,一种是java属性文件(键—值 ... -
单例模式的俩种方式
2012-01-04 14:09 937等等 单例模式的俩种方式: 饿汉式 class Singlet ... -
Mysql连接数据库:PreparedStatement.addBatch()方法
2011-12-23 08:43 58211.Eclipse连接MySQL数据库 mysql>C ... -
java中ArrayList 、LinkList区别
2011-12-21 15:30 1172java中ArrayList 、LinkList、List区别 ... -
Thread的实现
2011-12-21 14:25 1206Making a Thread A thread in Jav ... -
Date4j,一个简约的日期处理类库
2011-12-14 10:13 848Java本身的日期类在JDK1.0版本之后就再也没有更新过,同 ... -
How to use Log4j
2011-09-29 13:57 8121. LogManager.getInstance().get ... -
重写hashCode和equals方法(转)
2011-09-27 09:58 1136如果你的对象想散列存 ... -
学习Enum转
2011-09-22 14:11 8751. 关于 Java Enum: 学过 C/C++ 等 ... -
PO BO VO DTO POJO DAO概念及其作用(转)
2011-09-20 09:49 665J2EE开发中大量的专业缩略语很是让人迷惑,尤其是跟一些高手讨 ... -
Thread的实现
2011-09-20 09:47 859Thread的实现 1.extend Thread,then ... -
Adapter模式(转)
2011-08-17 15:48 890[b]GOF《设计模式》一书对Adapter模式是这样描述的: ... -
JVM常见配置汇总
2011-08-15 14:56 1904从这个图中可以看到, ... -
Abstract or Interface
2011-08-11 15:02 729详解java中的抽象类和接 ... -
HashMap HashTable TreeMap
2011-08-11 13:45 966Map中我们通过对象来对对象进行索引,用来索引的对象叫做key ... -
多线程死锁问题(转)
2011-08-10 19:42 586前天俺们谈到了加锁,但是在使用加锁的同时又会带来一个问题,就是 ...
相关推荐
在介绍双检锁模式(Double-Checked Locking Pattern,DCLP)的C++实现中,Scott Meyers和Andrei Alexandrescu在其2004年的文章中指出,传统的单例模式实现并不具备线程安全性。单例模式是设计模式中经常被提及的一种...
《C++ and the Perils of Double Checked Locking》是一篇探讨C++编程中双重检查锁定(Double-Checked Locking)模式潜在问题的文献。在多线程编程中,双重检查锁定是一种常见的优化策略,旨在减少对同步原语的依赖...
标题:C++与双检查锁定(Double Checked Locking)的陷阱 描述:C++如何解决单例模式的线程安全问题 ### 关键知识点解析: #### 单例模式的线程安全挑战 单例模式是一种设计模式,确保一个类只有一个实例,并提供...
本文将针对AngularJS中的ng-checked指令进行详细解析。 ng-checked指令是用于在AngularJS中动态控制复选框(checkbox)或单选按钮(radio)的选中状态。当你需要根据数据模型(model)中某个变量的真值(true)或假值(false...
在Java并发编程中,双检锁(Double-Checked Locking)是一种用于减少同步开销的优化技术,尤其适用于懒加载(lazy initialization)的场景。本文将详细探讨双检锁的工作原理、潜在问题以及如何安全地实现它。 双检锁...
在这个问题中,开发者遇到了一个关于`default-checked-keys`属性的问题,即当尝试将该属性设置为空数组时,树形控件的节点依然保持选中的状态,这与预期的行为不符。 `default-checked-keys`属性是Element UI Tree...
具体地,ng-checked指令调用了一个自定义函数isSelected,该函数接收一个id作为参数,如果该id存在于之前选中的结果数组中,则返回true,否则返回false。这样就能够根据之前的用户选择来控制复选框的显示状态。 ...
Software Adaptation in an Open Environment: A Software Architecture Perspective by Yu Zhou ...The organization and presentation of the book will be double-checked by professional scholars
北京火龙果软件工程技术中心意图无论什么时候当临界区中的代码仅仅需要加锁一次,同时当其获取锁的时候必须是线程安全的,可以用DoubleCheckedLocking模式来减少竞争和加锁载荷。动机1、标准的单例。开发正确的有效...
这里,`checked--effect-name`需要替换为23种动画效果中的一个,如`checked--pulse`或`checked--rotate`。 3. **自定义样式**:除了预设的动画效果,你还可以通过CSS对复选框和单选按钮的样式进行个性化调整,以...
然后,我们可以通过遍历$scope.grade.classes数组,查找is_checked为true的项目,从而得到所有选中的班级。 同样,对于radio,ng-checked的用法类似,只是每个radio对应一个唯一的值,而不是多选的情况。 总结起来...
双重检测锁(Double-Checked Locking)实现的Singleton模式在多线程应用中有相当的价值。在ACE的实现中就大量使用ACE_Singleton模板类将普通类转换成具有Singleton行为的类。这种方式很好地消除了一些重复代码臭味,...
各种工厂模式 242 第21章 Singleton模式和Double-Checked Locking模式 249 第22章 Object Pool模式 257 第23章 Factory Method模式 267 第24章 工厂模式的总结 272 第八部分 终点与起点 第25章 设计模式回顾:总结与...
多检查奇迹:checked伪类 polyfill(适用于 IE8 及以下)。如何使用包括 jQuery 1.x(与旧 IE 兼容)>= 1.7,然后在 IE 条件注释中包含 poly-checked 脚本: < script src =" //ajax.googleapis....
synchronized (DoubleCheckedLocking.class) { if (instance == null) { instance = new Instance(); } } } return instance; } } ``` 双重检查锁定的关键在于使用`volatile`关键字修饰`instance`变量。`...
代码片段: function isAllChecked(){ ... allChecked = $(this).find(":checked").not("[name='all-checked']") , thisAllCheckBtn = $(this).find(".all-checked [name='all-checked']");
为了提高性能,人们提出了**双重检查锁定**(Double-checked locking)的方法。这种方法首先在不加锁的情况下检查`instance`是否为`null`,如果为`null`则进行同步操作: ```java public static Singleton ...
}}在双重检查锁(Double-Checked Locking)实现中,第一次检查是在不加锁的情况下进行的,只有当第一次检查后 instance 仍然为 null 时,才会进入同步代码块进行第二次检查和实例化。这种方式提高了并发性能,因为...
3. **双端检锁(Double-Checked Locking, DCL)机制**: - DCL是一种尝试提高单例模式效率的设计模式,但如果没有正确同步,可能存在指令重排问题,导致非线程安全。 - 使用volatile可以禁止指令重排,从而在一定...