[concurrency-interest] syntax sugar for lazy instantiation

Ben Manes ben_manes at yahoo.com
Tue Jun 23 18:32:13 EDT 2009


While I understand the performance concern, I have not seen a problem in practice and the locking in FutureTask is well optimized.  It provides a nice idiom than sprinkling the code with double-checked locking tricks or introducing a new interface.  By using a future you leverage an existing interface and can utilize it to chain lower-level futures for performing post-processing logic on the client.  For example...

   // written ad-hoc...
   // Retrieves the entry in a remote caching layer where the key/value are use a custom serialization scheme
   // The client's future is bubbled up and performs post-processing to deserialize the value into object form
   // The "lazyFuture" provides the idiom to run the callable, which blocks on the inner future, on a Future#get().
    public <V> Future<V> get(K key) {
        String externalKey = serializer(key);
        final Future<String> future = remoteCache.get(externalKey);
        return Futures.lazyFuture(new Callable<V>() {
             public V call() throws Exception {
                 return deserializer(future.get());
             }
         });
    }

Because this idiom is generalized into a custom utility, Futures#lazyFuture(), the implementation can tuned globally if there is a performance problem.  Since I haven't experienced any noticeable overhead, the simplest approach for me was to rely on FutureTask until that problem occurs.  If a problem later occurs during performance testing, then implementing a Future with double-checked locking is simple, easy to test, and applies globally to all of my usages.




________________________________
From: Ashley Williams <ashpublic at mac.com>
To: Ben Manes <ben_manes at yahoo.com>
Cc: concurrency-interest at cs.oswego.edu
Sent: Tuesday, June 23, 2009 11:38:44 AM
Subject: Re: [concurrency-interest] syntax sugar for lazy instantiation


I'm going to have to think about this one since I've used futures but never connected them to double checked locks.
Fo instance I want to check that:

- I get fast access to field  99% of the time
- I only pay for lock when initializing a field.

I'm sure there are a ton of tricks like this, what would be great is an "effective threading" book for those that have
read the theory and want to get straight to idioms.

On 23 Jun 2009, at 19:19, Ben Manes wrote:

I use this trick all the time, but its already supplied via FutureTask.  It has very similar semantics to your code.  This trick can be extended to build lazy maps (and other lazy structures) which is also very convenient.

public class Example {
    private final FutureTask<Computation> future = new FutureTask<Computation>(new Computable());

    // Lazily computes the work and returns the value
    public Computation getComputation() throws Exception {
        future.run(); // no-ops on all subsequent calls
        return future.get();
    }

    private static final class Computable implements Callable<Computation> {
        public Computation call() {
          // do work, return
        }
    }
}




________________________________
From: Ashley Williams <ashpublic at mac.com>
To: concurrency-interest at cs.oswego.edu
Sent: Tuesday, June 23, 2009 10:05:24 AM
Subject: [concurrency-interest] syntax sugar for lazy instantiation


It seems to me that the only reason to use an intrinsic lock on a lazy getter (stick a synchronized keyword
on the method signature) is convenience of syntax. Please correct me if I am wrong.

So is one idea to simplify lazy instantiation, feedback welcome if anybody can improve on it. It's based
on the observation that double checked locking is mostly boilerplate except for the line that creates
the instance and so I've factored it out into a callable interface.

Admittedly the anonymous class makes for a more verbose constructor, but at least the dcl logic is
now encapsulated. And if java had support for closures then the readability problem would go away.

So first the class:

@ThreadSafe
public final class LazyField<T> {
private volatile T obj;
private final Callable<T> callable;

public LazyField(Callable<T> callable) {
this.callable = callable;
}

public T get() {
if (obj == null) {
synchronized (this) {
if (obj == null) {
obj = callable.call();
}
}
}
return obj;
}
}

and here is an example usage:


@ThreadSafe
public final class MyClass {
private final LazyField<String> message;


public MyClass() {
this.message = new LazyField<String>(new Callable<String>() {
public String call() throws Exception {
return "hello world";
}
});
}

public String getMessage() {
return message.get();
}
}

- Ashley Williams


      
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://cs.oswego.edu/pipermail/concurrency-interest/attachments/20090623/a3a10ad5/attachment-0001.html>


More information about the Concurrency-interest mailing list