commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject 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
Date Sat, 26 Sep 2009 14:27:33 GMT
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



Mime
View raw message