[LANG-496] Added LazyInitializer class plus test class.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@819141 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
31a812e4eb
commit
8958fdcc73
|
@ -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<ComplexObject> {
|
||||||
|
* @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();
|
||||||
|
}
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue