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:
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.
See Suppliers.memoize(Supplier)
to lazily initialize a value.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With