}
}
When a PrintServer is created, it creates a new Thread to do the actual printing and passes itself as the
Runnable instance. Starting a thread in a constructor in this way can be risky if a class can be extended. If it
becomes extended, the thread could access fields of the object before the extended class constructor had been
executed.
The requests queue takes care of synchronizing the different threads that access itthose calling print and
our own internal threadwe look at such synchronization in later sections and define the PrintQueue class
on page 356.
You may be wondering about the fact that we don't have a reference to the thread we createddoesn't that mean
that the thread can be garbage collected? The answer is no. While we didn't keep a reference to the thread,
when the thread itself was created, it stored a reference to itself in its ThreadGroupwe talk more about
thread groups later.
The work that you define within a run method is normally of a fairly private natureit should be performed
only by the worker to whom the work was assigned. However, as part of an interface, run is public and so
can be invoked indiscriminately by anyone with access to your objectsomething you usually don't desire. For
example, we definitely don't want clients to invoke the run method of PrintServer. One solution is to use
Thread.currentThread to establish the identity of the thread that invokes run and to compare it with
the intended worker thread. But a simpler solution is not to implement Runnable, but to define an inner
Runnable object. For example, we can rewrite PrintServer as follows:
class PrintServer2 {
private final PrintQueue requests = new PrintQueue();
public PrintServer2() {
Runnable service = new Runnable() {
public void run() {
for(;;)
realPrint(requests.remove());
}
};
new Thread(service).start();
}
public void print(PrintJob job) {
requests.add(job);
}
private void realPrint(PrintJob job) {
// do the real work of printing
}
}
The run method is exactly the same as before, but now it is part of an anonymous inner class that implements
Runnable. When the thread is created we pass service as the Runnable to execute. Now the work to be
performed by the thread is completely private and can't be misused.
Using Runnable objects you can create very flexible multithreaded designs. Each Runnable becomes a
unit of work and each can be passed around from one part of the system to another. We can store Runnable
objects in a queue and have a pool of worker threads servicing the work requests in the queuea very common
design used in multithreaded server applications.