Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lazy loading reference implementation

Being impressed by Guava's computing map feature, I'm searching for a sort of "computing reference" - a lazy loading reference implementation that parallel's Guava's ease of use, by which I mean it handles all locking, loading, and exception handling under the hood, only exposing a get() method.

After a brief search turned up nothing, I quickly rolled my own as a proof of concept:

public abstract class ComputingRef<T> implements Callable<T> {

   private volatile T referent = null;
   private Lock lock = new ReentrantLock();

   public T get() {
      T temp = referent;
      if (temp == null) {
         lock.lock();
         try {
            temp = referent;
            if (temp == null) {
               try {
                  referent = temp = call();
               }
               catch (Exception e) {
                  if (e instanceof RuntimeException) {
                     throw (RuntimeException)e;
                  }
                  else {
                     throw new RuntimeException(e);
                  }
               }
            }
         }
         finally {
            lock.unlock();
         }
      }
      return temp;
   }
}

This ComputingRef could be anonymously extended to implement call(), which functions as the factory method:

ComputingRef<MyObject> lazySingletonRef = new ComputingRef<MyObject>() {
   @Override
   public MyObject call() {
      //fetch MyObject from database and return
   }
};

I'm not satisfied that this implementation is optimal, but it demonstrates what I'm after.

I later found this example from the T2 Framework, which appears to be more complex.

Now my questions are:

  • How can my above code be improved?
  • How does it compare to the T2 example, and what advantages are offered by that example's greater complexity?
  • Are there other implementations of a lazy loading reference that I've missed in my search?

EDIT: Updated my implementation to use a local variable as suggested by @irreputable's answer - please upvote it if you find the above example useful.

like image 210
Paul Bellora Avatar asked Sep 23 '11 04:09

Paul Bellora


2 Answers

See Suppliers.memoize(Supplier) to lazily initialize a value.

like image 169
Ben Manes Avatar answered Sep 18 '22 08:09

Ben Manes


It's the good old double-checked locking idiom. You should add a local variable for performance. In your impl, you have 2 volatile reads in the fast path (when referent is set). Check http://en.wikipedia.org/wiki/Double-checked_locking

like image 42
irreputable Avatar answered Sep 21 '22 08:09

irreputable