[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