Java Reference
In-Depth Information
Implementation
Figure 4.5. Worker Thread class diagram
For the Worker Thread pattern, implement the following:
Client -
The client is responsible for creating the
Task
instances and putting the
Tasks
on the
Queue
.
Task -
The
Task
is the class that contains the work that needs to be executed. It implements the
java.lang.Runnable
interface.
Queue -
The
Queue
interface defines the methods by which the
Client
is able to hand off the
Tasks
and the
WorkerThread
to retrieve the
Tasks
.
ConcreteQueue -
The
ConcreteQueue
class implements the
Queue
interface and is responsible for storing and
retrieving the
Tasks
. It determines the order in which
Tasks
are provided to the
WorkerThread
.
WorkerThread -
The
WorkerThread
takes
Tasks
from the
Queue
and executes them. If there are no
Tasks
on
the
Queue
it waits. Because the
Queue
and the
WorkerThread
are tightly coupled, often the
WorkerThread
class
is an inner class of the
ConcreteQueue
class.
Benefits and Drawbacks
The
WorkerThread
influences performance in several ways.
The client no longer needs to create thread objects in order to run several tasks. It only needs to put the task on the
queue, which in performance is less expensive than creating a thread object.
A Thread that is not running is taking up performance because the scheduler still schedules the thread to be run, if
the thread is in a runnable state. Creating and starting a thread per task means that the scheduler has to schedule
each of these threads individually, which takes more time than when the scheduler has to schedule only one
worker thread. More threads means more scheduling. A task that is sitting in the queue and isn't running takes up
no time whatsoever.
The drawback of this design can occur when tasks are dependent on each other. Because the queue can be
sequential, the system can get into a deadlock. That's disastrous from a threading and from a performance point of
view.
There are a couple of possible solutions for this dilemma:
Make sure that there are as many worker threads as there are tasks that need to be run concurrently. That means
that you need to implement an expandable thread pool. The thread pool is discussed in the “
Pattern Variants
”
section.
Only allow tasks on the queue that do not depend on other tasks. Sometimes such behavior cannot be guaranteed.
In that case, the client cannot put the task on the queue, but has to instantiate its own thread or start another queue
with worker threads.