You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2009/09/26 16:27:33 UTC

svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Author: oheger
Date: Sat Sep 26 14:27:32 2009
New Revision: 819141

URL: http://svn.apache.org/viewvc?rev=819141&view=rev
Log:
[LANG-496] Added LazyInitializer class plus test class.

Added:
    commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java   (with props)
    commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/
    commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java   (with props)

Added: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java?rev=819141&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java (added)
+++ commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java Sat Sep 26 14:27:32 2009
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang.concurrent;
+
+/**
+ * <p>
+ * This class provides a generic implementation of the lazy initialization
+ * pattern.
+ * </p>
+ * <p>
+ * Sometimes an application has to deal with an object only under certain
+ * circumstances, e.g. when the user selects a specific menu item or if a
+ * special event is received. If the creation of the object is costly or the
+ * consumption of memory or other system resources is significant, it may make
+ * sense to defer the creation of this object until it is really needed. This is
+ * a use case for the lazy initialization pattern.
+ * </p>
+ * <p>
+ * This abstract base class provides an implementation of the double-check idiom
+ * for an instance field as discussed in Joshua Bloch's "Effective Java", 2nd
+ * edition, item 71. The class already implements all necessary synchronization.
+ * A concrete subclass has to implement the {@code initialize()} method, which
+ * actually creates the wrapped data object.
+ * </p>
+ * <p>
+ * As an usage example consider that we have a class {@code ComplexObject} whose
+ * instantiation is a complex operation. In order to apply lazy initialization
+ * to this class, a subclass of {@code LazyInitializer} has to be created:
+ *
+ * <pre>
+ * public class ComplexObjectInitializer extends LazyInitializer&lt;ComplexObject&gt; {
+ *     &#064;Override
+ *     protected ComplexObject initialize() {
+ *         return new ComplexObject();
+ *     }
+ * }
+ * </pre>
+ *
+ * Access to the data object is provided through the {@code get()} method. So,
+ * code that wants to obtain the {@code ComplexObject} instance would simply
+ * look like this:
+ *
+ * <pre>
+ * // Create an instance of the lazy initializer
+ * ComplexObjectInitializer initializer = new ComplexObjectInitializer();
+ * ...
+ * // When the object is actually needed:
+ * ComplexObject cobj = initializer.get();
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * If multiple threads call the {@code get()} method when the object has not yet
+ * been created, they are blocked until initialization completes. The algorithm
+ * guarantees that only a single instance of the wrapped object class is
+ * created, which is passed to all callers. Once initialized, calls to the
+ * {@code get()} method are pretty fast because no synchronization is needed
+ * (only an access to a <b>volatile</b> member field).
+ * </p>
+ *
+ * @version $Id$
+ * @param <T> the type of the object managed by this initializer class
+ */
+public abstract class LazyInitializer<T> {
+    /** Stores the managed object. */
+    private volatile T object;
+
+    /**
+     * Returns the object wrapped by this instance. On first access the object
+     * is created. After that it is cached and can be accessed pretty fast.
+     *
+     * @return the object initialized by this {@code LazyInitializer}
+     */
+    public T get() {
+        T result = object;
+
+        if (result == null) {
+            synchronized (this) {
+                result = object;
+                if (result == null) {
+                    object = result = initialize();
+                }
+            }
+        }
+
+        return result;
+    }
+
+    /**
+     * Creates and initializes the object managed by this {@code
+     * LazyInitializer}. This method is called by {@link #get()} when the object
+     * is accessed for the first time. An implementation can focus on the
+     * creation of the object. No synchronization is needed, as this is already
+     * handled by {@code get()}.
+     *
+     * @return the managed data object
+     */
+    protected abstract T initialize();
+}

Propchange: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java
URL: http://svn.apache.org/viewvc/commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java?rev=819141&view=auto
==============================================================================
--- commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java (added)
+++ commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java Sat Sep 26 14:27:32 2009
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.lang.concurrent;
+
+import java.util.concurrent.CountDownLatch;
+
+import junit.framework.TestCase;
+
+/**
+ * Test class for {@code LazyInitializer}.
+ *
+ * @version $Id$
+ */
+public class LazyInitializerTest extends TestCase {
+    /** The initializer to be tested. */
+    private LazyInitializerTestImpl initializer;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        initializer = new LazyInitializerTestImpl();
+    }
+
+    /**
+     * Tests obtaining the managed object.
+     */
+    public void testGet() {
+        assertNotNull("No managed object", initializer.get());
+    }
+
+    /**
+     * Tests whether sequential get() invocations always return the same
+     * instance.
+     */
+    public void testGetMultipleTimes() {
+        Object obj = initializer.get();
+        for (int i = 0; i < 10; i++) {
+            assertEquals("Got different object at " + i, obj, initializer.get());
+        }
+    }
+
+    /**
+     * Tests invoking get() from multiple threads concurrently.
+     */
+    public void testGetConcurrent() throws InterruptedException {
+        final int threadCount = 20;
+        final CountDownLatch startLatch = new CountDownLatch(1);
+        class GetThread extends Thread {
+            Object object;
+
+            @Override
+            public void run() {
+                try {
+                    // wait until all threads are ready for maximum parallelism
+                    startLatch.await();
+                    // access the initializer
+                    object = initializer.get();
+                } catch (InterruptedException iex) {
+                    // ignore
+                }
+            }
+        }
+
+        GetThread[] threads = new GetThread[threadCount];
+        for (int i = 0; i < threadCount; i++) {
+            threads[i] = new GetThread();
+            threads[i].start();
+        }
+
+        // fire all threads and wait until they are ready
+        startLatch.countDown();
+        for (Thread t : threads) {
+            t.join();
+        }
+
+        // check results
+        Object managedObject = initializer.get();
+        for (GetThread t : threads) {
+            assertEquals("Wrong object", managedObject, t.object);
+        }
+    }
+
+    /**
+     * A test implementation of LazyInitializer. This class creates a plain
+     * Object. As Object does not provide a specific equals() method, it is easy
+     * to check whether multiple instances were created.
+     */
+    private static class LazyInitializerTestImpl extends
+            LazyInitializer<Object> {
+        @Override
+        protected Object initialize() {
+            return new Object();
+        }
+    }
+}

Propchange: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain



Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by Oliver Heger <ol...@oliver-heger.de>.
sebb schrieb:
> On 28/09/2009, sebb <se...@gmail.com> wrote:
>> On 28/09/2009, Stephen Colebourne <sc...@btopenworld.com> wrote:
>>  > sebb wrote:
>>  >
>>  > > Where is that described? The first reference I found says that the
>>  > > local variable is a performance optimisation; it's the volatile
>>  > > keyword that makes it thread-safe:
>>  > >
>>  > >
>>  > http://technology.amis.nl/blog/4384/the-double-checked-locking-confusion
>>  > > also
>>  > >
>>  > http://jeremymanson.blogspot.com/2008/05/double-checked-locking.html
>>  > >
>>  > > But of course these could be wrong...
>>  > >
>>  >
>>  > http://java.sun.com/developer/technicalArticles/Interviews/bloch_effective_08_qa.html
>>  >
>>  >  "The idiom is very fast but also complicated and delicate, so don't be
>>  > tempted to modify it in any way"
>>  >
>>
>>
>> That does not necessarily mean that the version without the temporary
>>  variable is wrong.
>>
>>  As I see it, the fact that the variable is volatile is enough to
>>  ensure as-sequential operation, because the write of the volatile
>>  variable happens-before the read.
>>
> 
> I've just discovered that the book also says:
> 
> "This code may appear a bit convoluted.
> In particular, the need for the local variable <result> may be unclear ...
> While not strictly necessary this may improve performance ..."
> 
> So the local variable is indeed for performance reasons - it reduces
> the number of reads of the volatile variable to once in the case where
> the variable has already been initialised.
> 

Yes, this was also my understanding.

Well, obviously it is easy to get this pattern wrong or at least with 
reduced performance. This makes it a good candidate for a low-level 
library like [lang].

Oliver

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by sebb <se...@gmail.com>.
On 28/09/2009, sebb <se...@gmail.com> wrote:
> On 28/09/2009, Stephen Colebourne <sc...@btopenworld.com> wrote:
>  > sebb wrote:
>  >
>  > > Where is that described? The first reference I found says that the
>  > > local variable is a performance optimisation; it's the volatile
>  > > keyword that makes it thread-safe:
>  > >
>  > >
>  > http://technology.amis.nl/blog/4384/the-double-checked-locking-confusion
>  > > also
>  > >
>  > http://jeremymanson.blogspot.com/2008/05/double-checked-locking.html
>  > >
>  > > But of course these could be wrong...
>  > >
>  >
>  > http://java.sun.com/developer/technicalArticles/Interviews/bloch_effective_08_qa.html
>  >
>  >  "The idiom is very fast but also complicated and delicate, so don't be
>  > tempted to modify it in any way"
>  >
>
>
> That does not necessarily mean that the version without the temporary
>  variable is wrong.
>
>  As I see it, the fact that the variable is volatile is enough to
>  ensure as-sequential operation, because the write of the volatile
>  variable happens-before the read.
>

I've just discovered that the book also says:

"This code may appear a bit convoluted.
In particular, the need for the local variable <result> may be unclear ...
While not strictly necessary this may improve performance ..."

So the local variable is indeed for performance reasons - it reduces
the number of reads of the volatile variable to once in the case where
the variable has already been initialised.

>  >  Stephen
>  >
>  > ---------------------------------------------------------------------
>  >  To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
>  >  For additional commands, e-mail: dev-help@commons.apache.org
>  >
>  >
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by sebb <se...@gmail.com>.
On 28/09/2009, Stephen Colebourne <sc...@btopenworld.com> wrote:
> sebb wrote:
>
> > Where is that described? The first reference I found says that the
> > local variable is a performance optimisation; it's the volatile
> > keyword that makes it thread-safe:
> >
> >
> http://technology.amis.nl/blog/4384/the-double-checked-locking-confusion
> > also
> >
> http://jeremymanson.blogspot.com/2008/05/double-checked-locking.html
> >
> > But of course these could be wrong...
> >
>
> http://java.sun.com/developer/technicalArticles/Interviews/bloch_effective_08_qa.html
>
>  "The idiom is very fast but also complicated and delicate, so don't be
> tempted to modify it in any way"
>

That does not necessarily mean that the version without the temporary
variable is wrong.

As I see it, the fact that the variable is volatile is enough to
ensure as-sequential operation, because the write of the volatile
variable happens-before the read.

>  Stephen
>
> ---------------------------------------------------------------------
>  To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
>  For additional commands, e-mail: dev-help@commons.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by Stephen Colebourne <sc...@btopenworld.com>.
sebb wrote:
> Where is that described? The first reference I found says that the
> local variable is a performance optimisation; it's the volatile
> keyword that makes it thread-safe:
> 
> http://technology.amis.nl/blog/4384/the-double-checked-locking-confusion
> also
> http://jeremymanson.blogspot.com/2008/05/double-checked-locking.html
> 
> But of course these could be wrong...

http://java.sun.com/developer/technicalArticles/Interviews/bloch_effective_08_qa.html

"The idiom is very fast but also complicated and delicate, so don't be 
tempted to modify it in any way"

Stephen

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by sebb <se...@gmail.com>.
On 27/09/2009, Stephen Colebourne <sc...@btopenworld.com> wrote:
> >
> > > Is the temporary variable "result" needed?
> > >
> > > Or maybe I'm missing some subtlety here ?
> > > In which case it should be documented in a comment please.
> > >
> > > <snip/>
> > >
> > >
> >
> > According to Bloch's book the temporary variable is indeed a performance
> optimization. It prevents that the volatile field is read twice in the
> common case that the object has already been initialized.
> >
>
>  Its not about performance. Without the local variable, the pattern is
> unsafe concurrently. The pattern must be followed exactly (including the
> double assign) in order for the locking to be concurrent-safe.

Where is that described? The first reference I found says that the
local variable is a performance optimisation; it's the volatile
keyword that makes it thread-safe:

http://technology.amis.nl/blog/4384/the-double-checked-locking-confusion
also
http://jeremymanson.blogspot.com/2008/05/double-checked-locking.html

But of course these could be wrong...

>  Stephen
>
>
> ---------------------------------------------------------------------
>  To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
>  For additional commands, e-mail: dev-help@commons.apache.org
>
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by Stephen Colebourne <sc...@btopenworld.com>.
>> Is the temporary variable "result" needed?
>>
>> Or maybe I'm missing some subtlety here ?
>> In which case it should be documented in a comment please.
>>
>> <snip/>
>>
> 
> According to Bloch's book the temporary variable is indeed a performance 
> optimization. It prevents that the volatile field is read twice in the 
> common case that the object has already been initialized.

Its not about performance. Without the local variable, the pattern is 
unsafe concurrently. The pattern must be followed exactly (including the 
double assign) in order for the locking to be concurrent-safe.

Stephen

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by Oliver Heger <ol...@oliver-heger.de>.
sebb schrieb:
> On 26/09/2009, oheger@apache.org <oh...@apache.org> wrote:
>> Author: oheger
>>  Date: Sat Sep 26 14:27:32 2009
>>  New Revision: 819141
>>
>>  URL: http://svn.apache.org/viewvc?rev=819141&view=rev
>>  Log:
>>  [LANG-496] Added LazyInitializer class plus test class.
>>
>>  Added:
>>     commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java   (with props)
>>     commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/
>>     commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java   (with props)
> 
> <snip/>
> 
>>  +    /** Stores the managed object. */
>>  +    private volatile T object;
>>  +
>>  +    /**
>>  +     * Returns the object wrapped by this instance. On first access the object
>>  +     * is created. After that it is cached and can be accessed pretty fast.
>>  +     *
>>  +     * @return the object initialized by this {@code LazyInitializer}
>>  +     */
>>  +    public T get() {
>>  +        T result = object;
>>  +
>>  +        if (result == null) {
>>  +            synchronized (this) {
>>  +                result = object;
>>  +                if (result == null) {
>>  +                    object = result = initialize();
>>  +                }
>>  +            }
>>  +        }
>>  +
>>  +        return result;
>>  +    }
> 
> Is the temporary variable "result" needed?
> Would it not be simpler to do the following:
> 
> private volatile T object;
> 
> public T get() {
>       if (object == null) {
>          synchronized (this) {
>              if (object == null) {
>                  object = initialize();
>              }
>          }
>      }
>    return object;
> }
> 
> Or maybe I'm missing some subtlety here ?
> In which case it should be documented in a comment please.
> 
> <snip/>
> 

According to Bloch's book the temporary variable is indeed a performance 
optimization. It prevents that the volatile field is read twice in the 
common case that the object has already been initialized.

It's a good idea to add a comment. Will do.

Oliver

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r819141 - in /commons/proper/lang/trunk/src: java/org/apache/commons/lang/concurrent/LazyInitializer.java test/org/apache/commons/lang/concurrent/ test/org/apache/commons/lang/concurrent/LazyInitializerTest.java

Posted by sebb <se...@gmail.com>.
On 26/09/2009, oheger@apache.org <oh...@apache.org> wrote:
> Author: oheger
>  Date: Sat Sep 26 14:27:32 2009
>  New Revision: 819141
>
>  URL: http://svn.apache.org/viewvc?rev=819141&view=rev
>  Log:
>  [LANG-496] Added LazyInitializer class plus test class.
>
>  Added:
>     commons/proper/lang/trunk/src/java/org/apache/commons/lang/concurrent/LazyInitializer.java   (with props)
>     commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/
>     commons/proper/lang/trunk/src/test/org/apache/commons/lang/concurrent/LazyInitializerTest.java   (with props)

<snip/>

>
>  +    /** Stores the managed object. */
>  +    private volatile T object;
>  +
>  +    /**
>  +     * Returns the object wrapped by this instance. On first access the object
>  +     * is created. After that it is cached and can be accessed pretty fast.
>  +     *
>  +     * @return the object initialized by this {@code LazyInitializer}
>  +     */
>  +    public T get() {
>  +        T result = object;
>  +
>  +        if (result == null) {
>  +            synchronized (this) {
>  +                result = object;
>  +                if (result == null) {
>  +                    object = result = initialize();
>  +                }
>  +            }
>  +        }
>  +
>  +        return result;
>  +    }

Is the temporary variable "result" needed?
Would it not be simpler to do the following:

private volatile T object;

public T get() {
      if (object == null) {
         synchronized (this) {
             if (object == null) {
                 object = initialize();
             }
         }
     }
   return object;
}

Or maybe I'm missing some subtlety here ?
In which case it should be documented in a comment please.

<snip/>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org