Tuesday, February 8, 2011

Getting Around the Double-Checked Locking Problem

This is just a summary of alternatives to the double-checked locking pattern, which has been shown to have potential problems. The alternatives are taken from a CERT wiki article. The motivations for the pattern come from three concerns:

  1. avoid constructing an object that is expensive to construct, unless-until it is needed
  2. avoid more than one instantiation of that object, and
  3. avoid the overhead of synchronization for the more common case of fetching the object.


The simplest way to correct some legacy in-place double-check locking code is to simply declare the object in question as volatile - however, this only works with the newer Java memory model (since JDK 1.4), and (in my opinion) leaves some unnecessary code cruft in place (i.e., see below for more elegant alternatives):


class Foo {
private volatile Helper helper = null;

public Helper getHelper() {
if (helper == null) {
synchronized (this) {
if (helper == null) {
helper = new Helper(); // If the helper is null, create a new instance
}
}
}
return helper; // If helper is non-null, return its instance
}
}

Static initialization partially addresses the problem - it guarantees that the object is constructed at-most-once, but foregoes the lazy initialization - i.e, the object is constructed exactly once, whether it's needed or not:


class Foo {
private static final Helper helper = new Helper();

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

Initialize-on-demand addresses all three concerns:


class Foo {
private static class Holder {
static Helper helper = new Helper();
}
public static Helper getInstance() {
return Holder.helper;
}
}

With an immutable object, the Java memory model problems are also addressed by virtue of using final fields:


public final class Helper {
private final int n;
public Helper(int n) {
this.n = n;
}
// Other fields and methods, all fields are final. With this in place, the double-checked locking pattern is perfectly fine.
}

Finally, note that a standard otherwise-broken double-checked locking pattern is perfectly fine when initializing 32-bit primitives.

No comments:

Post a Comment