/*
* 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.lang3.concurrent;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
A class that allows complex initialization operations in a background task.
Applications often have to do some expensive initialization steps when they
are started, e.g. constructing a connection to a database, reading a
configuration file, etc. Doing these things in parallel can enhance
performance as the CPU load can be improved. However, when access to the
resources initialized in a background thread is actually required,
synchronization has to be performed to ensure that their initialization is
complete.
This abstract base class provides support for this use case. A concrete subclass must implement the initialize()
method. Here an arbitrary initialization can be implemented, and a result object can be returned. With this method in place the basic usage of this class is as follows (where MyBackgroundInitializer
is a concrete subclass):
MyBackgroundInitializer initializer = new MyBackgroundInitializer();
initializer.start();
// Now do some other things. Initialization runs in a parallel thread
...
// Wait for the end of initialization and access the result object
Object result = initializer.get();
After the construction of a BackgroundInitializer
object its start()
method has to be called. This starts the background processing. The application can now continue to do other things. When it needs access to the object produced by the BackgroundInitializer
it calls its get()
method. If initialization is already complete, get()
returns the result object immediately. Otherwise it blocks until the result object is fully constructed.
BackgroundInitializer
is a thin wrapper around a Future
object and uses an ExecutorService
for running the background initialization task. It is possible to pass in an ExecutorService
at construction time or set one using setExternalExecutor()
before start()
was called. Then this object is used to spawn the background task. If no ExecutorService
has been provided,
BackgroundInitializer
creates a temporary ExecutorService
and destroys it when initialization is complete.
The methods provided by BackgroundInitializer
provide for minimal interaction with the wrapped Future
object. It is also possible to obtain the Future
object directly. Then the enhanced functionality offered by Future
can be used, e.g. to check whether the background operation is complete or to cancel the operation.
Type parameters: - <T> – the type of the object managed by this initializer class
Since: 3.0
/**
* <p>
* A class that allows complex initialization operations in a background task.
* </p>
* <p>
* Applications often have to do some expensive initialization steps when they
* are started, e.g. constructing a connection to a database, reading a
* configuration file, etc. Doing these things in parallel can enhance
* performance as the CPU load can be improved. However, when access to the
* resources initialized in a background thread is actually required,
* synchronization has to be performed to ensure that their initialization is
* complete.
* </p>
* <p>
* This abstract base class provides support for this use case. A concrete
* subclass must implement the {@link #initialize()} method. Here an arbitrary
* initialization can be implemented, and a result object can be returned. With
* this method in place the basic usage of this class is as follows (where
* {@code MyBackgroundInitializer} is a concrete subclass):
* </p>
*
* <pre>
* MyBackgroundInitializer initializer = new MyBackgroundInitializer();
* initializer.start();
* // Now do some other things. Initialization runs in a parallel thread
* ...
* // Wait for the end of initialization and access the result object
* Object result = initializer.get();
* </pre>
*
* <p>
* After the construction of a {@code BackgroundInitializer} object its
* {@link #start()} method has to be called. This starts the background
* processing. The application can now continue to do other things. When it
* needs access to the object produced by the {@code BackgroundInitializer} it
* calls its {@link #get()} method. If initialization is already complete,
* {@link #get()} returns the result object immediately. Otherwise it blocks
* until the result object is fully constructed.
* </p>
* <p>
* {@code BackgroundInitializer} is a thin wrapper around a {@code Future}
* object and uses an {@code ExecutorService} for running the background
* initialization task. It is possible to pass in an {@code ExecutorService} at
* construction time or set one using {@code setExternalExecutor()} before
* {@code start()} was called. Then this object is used to spawn the background
* task. If no {@code ExecutorService} has been provided, {@code
* BackgroundInitializer} creates a temporary {@code ExecutorService} and
* destroys it when initialization is complete.
* </p>
* <p>
* The methods provided by {@code BackgroundInitializer} provide for minimal
* interaction with the wrapped {@code Future} object. It is also possible to
* obtain the {@code Future} object directly. Then the enhanced functionality
* offered by {@code Future} can be used, e.g. to check whether the background
* operation is complete or to cancel the operation.
* </p>
*
* @since 3.0
* @param <T> the type of the object managed by this initializer class
*/
public abstract class BackgroundInitializer<T> implements
ConcurrentInitializer<T> {
The external executor service for executing tasks. /** The external executor service for executing tasks. */
private ExecutorService externalExecutor; // @GuardedBy("this")
A reference to the executor service that is actually used. /** A reference to the executor service that is actually used. */
private ExecutorService executor; // @GuardedBy("this")
Stores the handle to the background task. /** Stores the handle to the background task. */
private Future<T> future; // @GuardedBy("this")
Creates a new instance of BackgroundInitializer
. No external ExecutorService
is used. /**
* Creates a new instance of {@code BackgroundInitializer}. No external
* {@code ExecutorService} is used.
*/
protected BackgroundInitializer() {
this(null);
}
Creates a new instance of BackgroundInitializer
and initializes it with the given ExecutorService
. If the ExecutorService
is not null, the background task for initializing this object will be scheduled at this service. Otherwise a new temporary
ExecutorService
is created. Params: - exec – an external
ExecutorService
to be used for task execution
/**
* Creates a new instance of {@code BackgroundInitializer} and initializes
* it with the given {@code ExecutorService}. If the {@code ExecutorService}
* is not null, the background task for initializing this object will be
* scheduled at this service. Otherwise a new temporary {@code
* ExecutorService} is created.
*
* @param exec an external {@code ExecutorService} to be used for task
* execution
*/
protected BackgroundInitializer(final ExecutorService exec) {
setExternalExecutor(exec);
}
Returns the external ExecutorService
to be used by this class. Returns: the ExecutorService
/**
* Returns the external {@code ExecutorService} to be used by this class.
*
* @return the {@code ExecutorService}
*/
public final synchronized ExecutorService getExternalExecutor() {
return externalExecutor;
}
Returns a flag whether this BackgroundInitializer
has already been started. Returns: a flag whether the start()
method has already been called
/**
* Returns a flag whether this {@code BackgroundInitializer} has already
* been started.
*
* @return a flag whether the {@link #start()} method has already been
* called
*/
public synchronized boolean isStarted() {
return future != null;
}
Sets an ExecutorService
to be used by this class. The
ExecutorService
passed to this method is used for executing the background task. Thus it is possible to re-use an already existing ExecutorService
or to use a specially configured one. If no ExecutorService
is set, this instance creates a temporary one and destroys it after background initialization is complete. Note that this method must be called before start()
; otherwise an exception is thrown. Params: - externalExecutor – the
ExecutorService
to be used
Throws: - IllegalStateException – if this initializer has already been
started
/**
* Sets an {@code ExecutorService} to be used by this class. The {@code
* ExecutorService} passed to this method is used for executing the
* background task. Thus it is possible to re-use an already existing
* {@code ExecutorService} or to use a specially configured one. If no
* {@code ExecutorService} is set, this instance creates a temporary one and
* destroys it after background initialization is complete. Note that this
* method must be called before {@link #start()}; otherwise an exception is
* thrown.
*
* @param externalExecutor the {@code ExecutorService} to be used
* @throws IllegalStateException if this initializer has already been
* started
*/
public final synchronized void setExternalExecutor(
final ExecutorService externalExecutor) {
if (isStarted()) {
throw new IllegalStateException(
"Cannot set ExecutorService after start()!");
}
this.externalExecutor = externalExecutor;
}
Starts the background initialization. With this method the initializer becomes active and invokes the initialize()
method in a background task. A BackgroundInitializer
can be started exactly once. The return value of this method determines whether the start was successful: only the first invocation of this method returns true,
following invocations will return false.
Returns: a flag whether the initializer could be started successfully
/**
* Starts the background initialization. With this method the initializer
* becomes active and invokes the {@link #initialize()} method in a
* background task. A {@code BackgroundInitializer} can be started exactly
* once. The return value of this method determines whether the start was
* successful: only the first invocation of this method returns <b>true</b>,
* following invocations will return <b>false</b>.
*
* @return a flag whether the initializer could be started successfully
*/
public synchronized boolean start() {
// Not yet started?
if (!isStarted()) {
// Determine the executor to use and whether a temporary one has to
// be created
ExecutorService tempExec;
executor = getExternalExecutor();
if (executor == null) {
executor = tempExec = createExecutor();
} else {
tempExec = null;
}
future = executor.submit(createTask(tempExec));
return true;
}
return false;
}
Returns the result of the background initialization. This method blocks until initialization is complete. If the background processing caused a runtime exception, it is directly thrown by this method. Checked exceptions, including InterruptedException
are wrapped in a ConcurrentException
. Calling this method before start()
was called causes an IllegalStateException
exception to be thrown. Throws: - ConcurrentException – if a checked exception occurred during
background processing
- IllegalStateException – if
start()
has not been called
Returns: the object produced by this initializer
/**
* Returns the result of the background initialization. This method blocks
* until initialization is complete. If the background processing caused a
* runtime exception, it is directly thrown by this method. Checked
* exceptions, including {@code InterruptedException} are wrapped in a
* {@link ConcurrentException}. Calling this method before {@link #start()}
* was called causes an {@code IllegalStateException} exception to be
* thrown.
*
* @return the object produced by this initializer
* @throws ConcurrentException if a checked exception occurred during
* background processing
* @throws IllegalStateException if {@link #start()} has not been called
*/
@Override
public T get() throws ConcurrentException {
try {
return getFuture().get();
} catch (final ExecutionException execex) {
ConcurrentUtils.handleCause(execex);
return null; // should not be reached
} catch (final InterruptedException iex) {
// reset interrupted state
Thread.currentThread().interrupt();
throw new ConcurrentException(iex);
}
}
Returns the Future
object that was created when start()
was called. Therefore this method can only be called after
start()
. Throws: - IllegalStateException – if
start()
has not been called
Returns: the Future
object wrapped by this initializer
/**
* Returns the {@code Future} object that was created when {@link #start()}
* was called. Therefore this method can only be called after {@code
* start()}.
*
* @return the {@code Future} object wrapped by this initializer
* @throws IllegalStateException if {@link #start()} has not been called
*/
public synchronized Future<T> getFuture() {
if (future == null) {
throw new IllegalStateException("start() must be called first!");
}
return future;
}
Returns the ExecutorService
that is actually used for executing the background task. This method can be called after start()
(before start()
it returns null). If an external executor
was set, this is also the active executor. Otherwise this method returns
the temporary executor that was created by this object.
Returns: the ExecutorService
for executing the background task
/**
* Returns the {@code ExecutorService} that is actually used for executing
* the background task. This method can be called after {@link #start()}
* (before {@code start()} it returns <b>null</b>). If an external executor
* was set, this is also the active executor. Otherwise this method returns
* the temporary executor that was created by this object.
*
* @return the {@code ExecutorService} for executing the background task
*/
protected final synchronized ExecutorService getActiveExecutor() {
return executor;
}
Returns the number of background tasks to be created for this initializer. This information is evaluated when a temporary
ExecutorService
is created. This base implementation returns 1. Derived classes that do more complex background processing can override it. This method is called from a synchronized block by the start()
method. Therefore overriding methods should be careful with obtaining other locks and return as fast as possible. Returns: the number of background tasks required by this initializer
/**
* Returns the number of background tasks to be created for this
* initializer. This information is evaluated when a temporary {@code
* ExecutorService} is created. This base implementation returns 1. Derived
* classes that do more complex background processing can override it. This
* method is called from a synchronized block by the {@link #start()}
* method. Therefore overriding methods should be careful with obtaining
* other locks and return as fast as possible.
*
* @return the number of background tasks required by this initializer
*/
protected int getTaskCount() {
return 1;
}
Performs the initialization. This method is called in a background task when this BackgroundInitializer
is started. It must be implemented by a concrete subclass. An implementation is free to perform arbitrary initialization. The object returned by this method can be queried using the get()
method. Throws: - Exception – if an error occurs
Returns: a result object
/**
* Performs the initialization. This method is called in a background task
* when this {@code BackgroundInitializer} is started. It must be
* implemented by a concrete subclass. An implementation is free to perform
* arbitrary initialization. The object returned by this method can be
* queried using the {@link #get()} method.
*
* @return a result object
* @throws Exception if an error occurs
*/
protected abstract T initialize() throws Exception;
Creates a task for the background initialization. The Callable
object returned by this method is passed to the ExecutorService
. This implementation returns a task that invokes the initialize()
method. If a temporary ExecutorService
is used, it is destroyed at the end of the task. Params: - execDestroy – the
ExecutorService
to be destroyed by the task
Returns: a task for the background initialization
/**
* Creates a task for the background initialization. The {@code Callable}
* object returned by this method is passed to the {@code ExecutorService}.
* This implementation returns a task that invokes the {@link #initialize()}
* method. If a temporary {@code ExecutorService} is used, it is destroyed
* at the end of the task.
*
* @param execDestroy the {@code ExecutorService} to be destroyed by the
* task
* @return a task for the background initialization
*/
private Callable<T> createTask(final ExecutorService execDestroy) {
return new InitializationTask(execDestroy);
}
Creates the ExecutorService
to be used. This method is called if no ExecutorService
was provided at construction time. Returns: the ExecutorService
to be used
/**
* Creates the {@code ExecutorService} to be used. This method is called if
* no {@code ExecutorService} was provided at construction time.
*
* @return the {@code ExecutorService} to be used
*/
private ExecutorService createExecutor() {
return Executors.newFixedThreadPool(getTaskCount());
}
private class InitializationTask implements Callable<T> {
Stores the executor service to be destroyed at the end. /** Stores the executor service to be destroyed at the end. */
private final ExecutorService execFinally;
Creates a new instance of InitializationTask
and initializes it with the ExecutorService
to be destroyed at the end. Params: - exec – the
ExecutorService
/**
* Creates a new instance of {@code InitializationTask} and initializes
* it with the {@code ExecutorService} to be destroyed at the end.
*
* @param exec the {@code ExecutorService}
*/
InitializationTask(final ExecutorService exec) {
execFinally = exec;
}
Initiates initialization and returns the result.
Throws: - Exception – if an error occurs
Returns: the result object
/**
* Initiates initialization and returns the result.
*
* @return the result object
* @throws Exception if an error occurs
*/
@Override
public T call() throws Exception {
try {
return initialize();
} finally {
if (execFinally != null) {
execFinally.shutdown();
}
}
}
}
}