and in the libraries that come with the JDK. In this chapter, well discuss why threading is important in distributed programming and cover the fundamentals of using threads in Java.
11.1 More Than One Client
In our previous discussions on deciding on a server, I talked briefly about several issues related to scalability. The goal of those discussions was simple: we wanted to guarantee that our
systems will be able to simultaneously support many different users, all of whom are attempting to perform different tasks.
But those discussions carefully dodged a very difficult question: how do we build a server that can handle more than one request at once? Consider the bank system we built in
Chapt er 9 . If we
take the existence of multiple clients into account, we wind up with the architecture diagram shown in
Figur e 11- 1 .
Figure 11-1. Architecture diagram for the bank system
This has an important implication: the possibility exists that two separate clients will receive stubs for the same instance of
Account e.g., they request the same server from the registry and then
simultaneously attempt to perform banking operations on it. This could be undesirable, for example, if both clients attempt to withdraw money from the same account at the same time.
This seems like an easily solved problem. Its analogous, in the real world, to having more than one client in the bank at the same time. In the real world, the clients form a line and wait until a
teller becomes available. And, at first glance, that seems like what we did with the socket-based printer server. Recall how we implemented the method that accepted print requests:
public void accept { while true {
Socket clientSocket = null; try {
clientSocket = _serverSocket.accept ; blocking call
processPrintRequestclientSocket; ....
} When one client connects, the request is processed, and our code resumes listening for other
connections. Consequently, while a print request is processed, all the other print requests simply wait. The requests are trapped in the operating-system layer and never even get to the server
until the server is ready to handle them.
However, this similarity is deceptive. There are two crucial differences between what happens in the bank and what we implemented. The first is that we never really implemented any sort of
next in line functionality. That is, our socket-based printer server doesnt guarantee a first-come, first-served policy unless the operating system does. Moreover, if a request is complex and ties
up the printer server for a significant period of time, most of the client requests will simply time out.
The second difference is similar: the server offers no feedback. In the real world, you can see how long the line is, and how fast the line is moving. Based on that information, you can decide to
do something else. The socket code simply offers you the opportunity to wait, without knowing how long youll wait or whether youll eventually time out.
In general, the put all the clients in a queue solution isnt even a good policy in the real world. Consider our real-world bank example and suppose that the following situation occurs:
Mr. Jones steps up to the tellers window. He attempts to withdraw 100,000, a sum large enough to require a branch managers approval before being
processed. The branch manager, however, has stepped out to the corner store to buy some cigarettes.
What happens in the real world in this case? The teller would ask Mr. Jones to stand to the side, promising that as soon as the manager returns and approves his request, he will handle his
business. The teller would then proceed to handle the next customer in line. Mr. Jones might be a little unhappy with this delay, but at least the other people in line wont have to wait for the branch
manager as well.
We can translate this scenario into computer terms: A client makes a request on a remote server that requires the use of a currently
unavailable scarce resource. Rather than blocking all other clients from making requests of the server, the current clients request is postponed, and other client
requests are handled until the resource becomes available.
This might seem like a contrived example, but consider our printer server again. Suppose that a client submits a complex 37-page document that will take 11 minutes to print. The wait-in-line
solution then says: no one else can submit print jobs for the next 11 minutes. What wed really rather have happen is:
1. The first client attempts to submit the print job. 2. The server accepts the print job and starts printing the document.
3. The second client submits its print job. 4. The server accepts the second print job but also informs the second client that the
current wait is rather long, giving the client an estimate as to when the printed document will be available.
5. The client can then monitor the printers status, sending more method calls to the printer while the printer is still printing other documents, and perhaps cancel its request.
Nailing this down a little further, what we really want in this scenario is an implementation of the following rough description:
When a print request is received, take the entire document and put it into a print queue on the server machine. Immediately return to handle the next remote
method call. At the same time, since marshalling and demarshalling a document can be a lengthy process, and since the document may take a long time to be
sent over the network, continue to simultaneously accept other documents and respond to method calls that are querying the servers status.
The second part of this description At the same time... may still seem a little unmotivated. Why do we need to be able to accept more than one document simultaneously? The reason is that our
application is a distributed application. Network latency and partial failure can cause difficulties, as in the following scenario:
Bob is working from home this morning on an important presentation. He puts the finishing touches on it, connects to the Internet, and sends the document to the
company printer. He then heads out for lunch, planning to pick up the printed document later in the day.
In this scenario, network latency can cause the printer to become unavailable for long periods of time. Bobs presentation is probably quite large. And sending it over the Internet, from a dialup
connection, might take a long time. If we dont insist that the printer handle simultaneous connections, then while Bob is sending the document, the printer will be idle and not accept other
documents. Not only do we have the feedback problems we mentioned earlier i.e., other clients dont have any idea when, and if, they will be able to submit documents, this is also an inefficient
use of the printer.
The partial failure scenario causes a similar problem. Bobs connection could go down for a little while before the printer server realizes anything is wrong. In which case, not only does the printer
server not accept documents, it no longer receives Bobs print request.
As you can see, the ability to service requests from more than one client, and to do so at the same time, is crucial in the world of distributed applications. And the way it is done is by the use
of threads.
[ 1] [ 1]
Thread is shorthand for thread of execution. With the exception of overly z ealous undergraduates, very few people use the full name.
11.2 Basic Terminology