Defeat of Double-checked Locking

      Java 2004-6-18 21:15
DCL(Double-checked Locking)是Lazy-loading时避免过度同步(多线程考虑)采用的技巧。下面是个例子:
class SomeClass {
private Resource resource = null;

public Resource getResource() {
if (resource == null) {
synchronized(this) {
if (resource == null)
resource = new Resource();
}
}
return resource;
}
}

这样做避免在getResource()方法上进行synchronized,而且调用getResource()不会每次都进行synchronized,提高了效率。但它真的解决了同步问题吗?没有!详细参见下面文章:
Warning! Threading in a multiprocessor world
Find out why many tricks to avoid synchronization overhead just don't work
www.javaworld.com/javaworld/jw-02-2001/jw-0209-toolbox.html

Double-checked locking: Clever, but broken
www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html

Can double-checked locking be fixed?
No matter how you rig it, double-checked locking still fails
www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html

The "Double-Checked Locking is Broken" Declaration
www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

有两种代替方法:
class SomeClass {
private Resource resource = null;

public synchronized static Resource getResource() {
if (resource == null) {
resource = new Resource();
}
return resource;
}
}

class SomeClass {
private static Resource resource = new Resource();

public static Resource getResource() {
return resource;
}
}

或者使用ThreadLocal加DCL的方法,但因为ThreadLocal本身的性能问题,采用时也要考虑。如果采用JDK 1.4.2以上,ThreadLocalDCL方式是比直接在方法上synchronized效率高而且安全的。
class ThreadLocalDCL {
private static ThreadLocal initHolder = new ThreadLocal();
private static Resource resource = null;

public Resource getResource() {
if (initHolder.get() == null) {
synchronized(this) {
if (resource == null)
resource = new Resource();
initHolder.set(Boolean.TRUE);
}
}
return resource;
}
}

详细请看下面文章:
Can ThreadLocal solve the double-checked locking problem?
ThreadLocal appears to fix the thread-safety issues behind double-checked locking
www.javaworld.com/javaworld/jw-11-2001/jw-1116-dcl.html

还有一种方式,是否解决问题:

public final class FooSingleton {
// 开始时不会被加载,直到调用getInstance()时才加载
private static final class SingletonHolder {
static final FooSingleton _singleton = new FooSingleton ();
}

private FooSingleton () {
}

public static FooSingleton getInstance () {
return SingletonHolder._singleton;
}
}

不过不知道是否适合一些属性是Lazy-loading的情况?

And how about this one?
public class Utopia {
private Foo instance = null;

public Foo getFoo() {
if (instance != null)
return instance;
// (this) is redundant, but included to emphasize that we're NOT sync'ing on instance.
synchronized<instance>(this) {
// at this point, we'll see the old value of 'instance' if no thread has changed it
// from inside this synchronized block, or its most recent value if some prior thread
// executing within this block has given it a new value.
if (instance == null)
instance = new Foo();

// we MUST do the return here, because instance's new value has not necessarily been
// propagated out to the "real" 'instance' variable visible to the outside world yet...
return instance;
}
// at some point in the future, the new value of instance will be atomically
// propagated to its namesake "out here"
}
}

关于这个问题,最终还是没有找到定论。但是,DCL是有隐患的,所以不用为妙。
标签集:TAGS:
回复Comments() 点击Count()

回复Comments

{commentauthor}
{commentauthor}
{commenttime}
{commentnum}
{commentcontent}
作者:
{commentrecontent}
}