img
. . .
A small number of high-level design strategies have been discussed in several topics (see The
Authors on the Net ). These names are not used completely uniformly. They are:
·
Master/Slave: One thread does the main work of the program, creating other threads to
help in some portion of the work.
·
Client/Server (Thread per Request): One thread listens for requests, then creates a new
thread to handle each request.
·
Producer/Consumer (a.k.a. Work Queue or Workpile or Thread Pool): Some threads
create work requests and put them on a queue. Other threads take the work requests off
the queue and execute them.
·
Pipeline: Each thread does some work on a task, then passes the partially completed task
to the next thread.
·
Client/Server (Thread per Client): One thread listens for new clients to attach, then
creates a new thread to handle each client. The thread is dedicated to its client, doing
work only for that client.
In the discussion below we will elaborate on each of the designs and include some sample code.
All the code will be based on a client/server program that takes in requests from a socket,
processes them, and sends replies back out over the same socket file descriptor. The complete
code for three versions of the program (thread per request, producer/consumer, and nonthreaded)
is on the Web site.
Master/Slave
The master/slave design is the most obvious for many kinds of tasks. In its most elemental form, it
will be implemented by a library, and the programmer will not even be aware of there being
multiple threads. A matrix multiply routine may well spawn a set of threads to do the work, but all
the programmer knows is that she called matrix_multiply().
Client/Server (Thread per Request)
This is really just a master/slave design for client/server programs. The master thread will do the
listening. In the fragment of the socket program shown in Code Example 12-7, each time a new
request comes in from a client, the main thread spawns off a thread to handle that request. The
main thread then returns to its accept() loop while the thread works on the request
independently, exiting when it's done.
Example 12-7 Master/Slave Socket Design
public void runServer() throws Exception { // Executes in main thread
for (int i = 1; true; i++) {
socket = serverSocket.accept();
Thread t = new Thread(new ProcessRequest(socket));
t.start();
}
}
public void process() {
int n = csocket.is.read(request); // request = "Request ..."
reply = getReply(request);
csocket.os.write(reply); // reply = "Reply ..."
}
Although this design has some positive aspects (e.g., simplicity and directness), it also admits to
some drawbacks. The cost of thread creation is not going to be significant unless the task itself is
very short (< 10 ms). Of more significance is that the programmer has no simple control over the
Search WWH :
Custom Search
Previous Page
Multithreaded Programming with JAVA - Topic Index
Next Page
Multithreaded Programming with JAVA - Bookmarks
Home