开发者

LazyReference with double-checked locking and null handling

I've been using LazyReference class for a few years (not on a regular basis of course, but sometimes it is very useful). The class can be seen here. Credits go to Robbie Vanbrabant (class author) and Joshua Bloch with his famous "Effective Java 2nd edt." (original code).

The class works correctly (in Java 5+) but there is one little potential issue. If instanceProvider returns null (well it must not according to Guice Provider.get() contract, but…) then on every execution of LazyReference.get() method the LOCK will be held and instanceProvider.get will be called over and over again. It looks like a good punishment for those who break the contracts (he-he), but what if one really needs to lazily initialize a field with the possibility to set the开发者_开发技巧 null value?

I modified LazyReference a little bit:

public class LazyReference<T> {

  private final Object LOCK = new Object();

  private volatile T instance;

  private volatile boolean isNull;

  private final Provider<T> instanceProvider;

  private LazyReference(Provider<T> instanceProvider) {
    this.instanceProvider = instanceProvider;
  }

  public T get() {
    T result = instance;
    if (result == null && !isNull) {
      synchronized (LOCK) {
        result = instance;
        if (result == null && !isNull) {
          instance = result = instanceProvider.get();
          isNull = (result == null);
        }
      }
    }
    return result;
  }
}

IMHO it should work just fine (if you have another opinion please post your comments and criticisms). But I wonder what will happen if I remove the volatile modifier from isNull boolean (leaving it for instance of course)? Will it still work correctly?


The above code has a race condition: instance may be set to the "real" null from the result of instanceProvider.get() before isNull has been set.

Are you sure it wouldn't be easier for you to just scrap this complicated nonsense and synchronise properly. I bet you will not be able to measure any difference in performance and it will be easier to verify that your code is correct.


As pointed out by Neil Coffey, this code contains a race condition, but it can be easily fixed as follows (note that instance don't need to be volatile):

public class LazyReference<T> {     
  private T instance;
  private volatile boolean initialized;
  ...
  public T get() {
    if (!initialized) {
      synchronized (LOCK) {
        if (!initialized) {
          instance = instanceProvider.get();
          initialized = true;
        }
      }
    }
    return instance;
  }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜