[concurrency-interest] syntax sugar for lazy instantiation

Jed Wesley-Smith jed at atlassian.com
Tue Jun 23 22:37:34 EDT 2009


The Java6 source code can be downloaded as the "Developer Documentation" 
from http://developer.apple.com/java/download/

We have a LazyReference[1] class used extensively internally that 
encapsulates all the required functionality. It is based on FutureTask 
as well, and is fully optimised[2] for performance.

I am very wary when people try to optimise for space over correctness 
and even readability. Concurrency is so hard for most developers that 
favouring correctness over space (unless space is proven to be the first 
order issue) seems like premature optimisation.

cheers,
jed.

[1] LazyReference from the atlassian-util-concurrent library
source: 
http://labs.atlassian.com/source/browse/CONCURRENT/trunk/src/main/java/com/atlassian/util/concurrent/LazyReference.java?r=2605
doc: http://labs.atlassian.com/wiki/display/CONCURRENT/LazyReference
[2] FutureTask.run() is a no-op if already run, but this is implemented 
by a CAS update on the state which can perform worse than a simple read 
of the state (isDone()) under certain circumstances.

Ashley Williams wrote:
> I guess what's making it hard for me to comment on this is that I 
> don't have the source code for FutureTask so I have no idea
> what it is doing under the hood (thanks Apple). I agree it would be 
> nice not to have to introduce a new class and if the
> "happy path" is just a null check as with DCL then I would definitely 
> see the benefit of this approach.
>
> The only thing that makes me a little uncomfortable is that I guess 
> FutureTask must contain more than one field to record
> the exception, callable and result for example. Therefore I would be 
> effectively swapping a single field with multiple.
> I'm not saying this is a deal breaker though.
>
> On 23 Jun 2009, at 23:32, Ben Manes wrote:
>
>> 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 <mailto:ashpublic at mac.com>>
>> *To:* Ben Manes <ben_manes at yahoo.com <mailto:ben_manes at yahoo.com>>
>> *Cc:* concurrency-interest at cs.oswego.edu 
>> <mailto: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 <mailto:ashpublic at mac.com>>
>>> *To:* concurrency-interest at cs.oswego.edu 
>>> <mailto: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
>>>
>>>
>>
>>
>>
>
> ------------------------------------------------------------------------
>
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at cs.oswego.edu
> http://cs.oswego.edu/mailman/listinfo/concurrency-interest
>   



More information about the Concurrency-interest mailing list