Java Reference
In-Depth Information
The
java.util.concurrent.Executors
class provides static factory methods for creating executor ob-
jects. I introduce you to
java.util.concurrent.ExecutorService
objects as an example of how you can
use an executor. You can create an
ExecutorService
object like this:
ExecutorService threadPool = Executors.newCachedThreadPool();
This creates an object that creates new threads as required. Each time you use the
threadPool
object to
start another task, either an existing thread in the pool that is currently no longer running is used, or a new
thread is created and added to the thread pool. If you want to limit the number of threads that might be cre-
ated, you can create a thread pool with a fixed number of threads, like this:
int nThreads = 5;
ExecutorService threadPool = Executors.newFixedThreadPool(nThreads);
This fragment creates a thread pool that contains up to
nThreads
threads. Starting a new task using
the
threadPool
object results in the task being executed in a new thread as long as there are fewer than
nThreads
threads already in operation. If all threads in the pool have been allocated, the new task is placed
in a queue to wait until one of the existing threads becomes free. All the threads in the pool continue to exist
until
threadPool
is shut down. The method throws an exception of type
IllegalArgumentException
if
you specify the maximum number of threads to be less than 1.
ExecutorService
is an interface type that is implemented by several classes in the
java.util.concurrent
package. Both of the methods from the
Executors
class that I have introduced
return a reference to an object of type
ThreadPoolExecutor
, and the functionality is described by the
Ex-
ecutorService
interface. Let's look at how you use a
ThreadPoolExecutor
object.
Working with an Executor
The methods specified by the
ExecutorService
interface enable you to submit new tasks to be executed in
a new thread or an existing unused thread within a thread pool. The interface declares three versions of the
submit()
method: two for starting tasks that are
Runnable
objects and one for starting
Callable<>
tasks.
You start a
Runnable
task like this:
ExecutorService threadPool = Executors.newCachedThreadPool();
Future<?> future = threadPool.submit(clerk1);
This fragment starts the
Runnable
task,
clerk1
, that is a
Clerk
object from the previous example. In
general, the
submit()
method returns a reference of type
java.util.concurrent.Future<V>
that reflects
the state of the task at any time, and eventually encapsulates the result of executing a task, the result being of
type
V
. A
Runnable
task does not return a result so the value returned by submit will be of type
Future<?>
.
The second submit() method for a
Runnable
task accepts a second argument of type V that specifies the
result that is to be returned when the task completes. This enables you to ensure that the result you specify
is encapsulated by the
Future<V>
object when the task finishes.
I'll discuss the other version of the
submit()
method and then return to the question of what you can do
with a
Future<V>
object.
Executing Callable<V> Tasks