Java Reference
In-Depth Information
server crash if the traffic load ever reaches a certain threshold. For a server application that
is supposed to provide high availability and graceful degradation under load, this is a serious
failing.
6.2. The Executor Framework
Tasks are logical units of work, and threads are a mechanism by which tasks can run asyn-
chronously. We've examined two policies for executing tasks using threads—execute tasks
sequentially in a single thread, and execute each task in its own thread. Both have serious
limitations: the sequential approach suffers from poor responsiveness and throughput, and the
thread-per-task approach suffers from poor resource management.
In
Chapter 5
, we saw how to use
bounded queues
to prevent an overloaded application from
running out of memory.
Thread pools
offer the same benefit for thread management, and
java.util.concurrent
provides a flexible thread pool implementation as part of the
Executor
framework. The primary abstraction for task execution in the Java class libraries
Listing 6.3.
Executor
Interface.
Executor
may be a simple interface, but it forms the basis for a flexible and powerful
framework for asynchronous task execution that supports a wide variety of task execution
policies. It provides a standard means of decoupling
task submission
from
task execution
,
describing tasks with
Runnable
. The
Executor
implementations also provide lifecycle
support and hooks for adding statistics gathering, application management, and monitoring.
Executor
is based on the producer-consumer pattern, where activities that submit tasks are
the producers (producing units of work to be done) and the threads that execute tasks are the
consumers (consuming those units of work).
Usingan
Executor
isusually theeasiest path
to implementing a producer-consumer design in your application.