CHAPTER 12. THREADS AND MULTIPROCESSING
627
In fact, connections in the queue are waiting anyway; they are not being serviced. If the
queue grows unreasonably long, connections in the queue will have to wait for an unreasonable
amount of time. If the queue keeps growing indefinitely, that just means that the server is
receiving connection requests faster than it can process them. That could happen for several
reasons: Your server might simply not be powerful enough to handle the volume of traffic that
you are getting; you need to buy a new server. Or perhaps the thread pool doesn’t have enough
threads to fully utilize your server; you should increase the size of the thread pool to match the
server’s capabilities. Or maybe your server is under a “Denial Of Service” attack, in whichsome
bad guy is deliberately sending your server more requests than it can handle in an attempt to
keep other, legitimate clients from getting service.
In any case, ArrayBlockingQueue with limited capacity is the correct choice. The queue
should be short enough so that connections in the queue will not have to wait too long for
service. In a real server, the size of the queue and the number of threads in the thread pool
should be adjusted to “tune” the server to account for the particular hardware and network on
which the server is running and for the nature of the client requests that it typically processes.
Optimal tuning is, in general, a difficult problem.
There is, by the way, another way that things can go wrong: Suppose that the server needs
to read some data from the client, but the client doesn’t send the expected data. The thread
that is trying to read the data can then block indefinitely, waiting for the input. If a thread
pool is being used, this could happen to every thread in the pool. In that case, no further
processing can ever take place! The solution to this problem is to have connections “time out”
if they are inactive for an excessive period of time. Typically, each connection thread will keep
track of the time when it last received data from the client. The server runs another thread
(sometimes called a “reaper thread”, after the Grim Reaper) that wakes up periodically and
checks each connection thread to see how long it has been inactive. A connection thread that
has been waiting too long for input is terminated, and a new thread is started in its place. The
question of how long the timeout period should be is another difficult tuning issue.
12.4.5 Distributed Computing
We have seen how threads can be used to do parallel processing, where a number of processors
work together to complete some task. So far, we have assumed that all the processors were
inside one multi-processor computer. But parallel processing can also be done using processors
that are in different computers, as long as those computers are connected to a network over
whichthey cancommunicate. This type of parallelprocessing—in whicha number of computers
work together on a task and communicate over a network—is called distributed computing.
In some sense, the whole Internet is an immense distributed computation, but here I am
interested in how computers on a network can cooperate to solve some computational problem.
There are several approaches to distributed computing that are supported in Java. RMI and
CORBA are standards that enable a program running on one computer to call methods in
objects that exist on other computers. This makes it possible to design an object-oriented
program in which different parts of the program are executed on different computers. RMI
(Remote Method Invocation) only supports communication between Java objects. CORBA
(Common Object Request Broker Architecture) is a more general standard that allows objects
written in various programming languages, including Java, to communicate with each other.
As is commonly the case in networking, there is the problem of locating services (where in this
case, a “service” means an object that is available to be called over the network). That is, how
can one computer know which computer a service is located on and what port it is listening
Pdf security options - C# PDF Digital Signature Library: add, remove, update PDF digital signatures in C#.net, ASP.NET, MVC, WPF
Help to Improve the Security of Your PDF File by Adding Digital Signatures
create pdf security; decrypt a pdf
Pdf security options - VB.NET PDF Digital Signature Library: add, remove, update PDF digital signatures in vb.net, ASP.NET, MVC, WPF
Guide VB.NET Programmers to Improve the Security of Your PDF File by Adding Digital Signatures
change security on pdf; pdf password unlock
CHAPTER 12. THREADS AND MULTIPROCESSING
628
on? RMI and CORBA solve this problem using a “request broker”—a server program running
at a known location keeps a list of services that are available on other computers. Computers
that offer services register those services with the request broker; computers that need services
contact the broker to find out where they are located.
RMI and CORBA are complex systems that are not very easy to use. I mention them here
because they are part of Java’s standard network API, but I will not discuss them further.
Instead, we will look at a relatively simple demonstration of distributed computing that uses
only basic networking.
The problemthat we willconsideristhe same one that we usedinMultiprocessingDemo1.java,
and its variations, inSection12.2 andSection12.3, namely the computation of a complex im-
age. This is an application that uses the simplest type of parallel programming, in which the
problem can be broken down into tasks that can be performed independently, with no commu-
nication between the tasks. To apply distributed computing to this type of problem, we can
use one “master” program that divides the problem into tasks and sends those tasks over the
network to “worker” programs that do the actual work. The worker programs send their results
back to the master program, which combines the results from all the tasks into a solution of
the overall problem. In this context, the worker programs are often called “slaves,” and the
program uses the so-called master/slave approach to distributed computing.
The demonstration program is defined by three source code files:CLMandelbrotMaster.java
defines the master program; CLMandelbrotWorker.java defines the worker programs; and
CLMandelbrotTask.java defines the class, , CLMandelbrotTask, , that t represents s an individual
task that is performed by the workers. To run the demonstration, you must start the
CLMandelbrotWorker program on several computers (probably by running it on the command
line). This program uses CLMandelbrotTask, so both class files, CLMandelbrotWorker.class
and CLMandelbrotTask.class, must be present on the worker computers. You can then run
CLMandelbrotMaster on the master computer. Note that this program also requires the class
CLMandelbrotTask. You must specify the host name or IP address of each of the worker com-
puters as command line arguments for CLMandelbrotMaster. A worker program listens for
connection requests from the master program, and the master program must be told where
to send those requests. For example, if the worker program is running on three comput-
ers with IP addresses 172.30.217.101, 172.30.217.102, and 172.30.217.103, then you can run
CLMandelbrotMaster with the command
java CLMandelbrotMaster 172.30.217.101 172.30.217.102 172.30.217.103
The master will make a network connection to the worker at each IP address; these connections
will be used for communication between the master program and the workers.
It is possible to run several copies of CLMandelbrotWorker on the same computer, but
they must listen for network connections on different ports. It is also possible to run
CLMandelbrotWorker on the same computer as CLMandelbrotMaster. You might even see
some speed-up when you do this, if your computer has several processors. See the comments in
the program source code files for more information, but here are some commands that you can
use to run the master program and two copies of the worker program on the same computer.
Give these commands in separate command windows:
java CLMandelbrotWorker
(Listens on default port)
java CLMandelbrotWorker 2501
(Listens on port 2501)
java CLMandelbrotMaster localhost localhost:2501
.NET PDF SDK - Description of All PDF Processing Control Feastures
Easy to change PDF original password; Options for setting PDF security level; PDF text content, image and pages redact options. PDF Digital Signature.
convert secure pdf to word; pdf secure signature
.NET Annotation SDK| Annotate, Redact Images
Rich options to move, resize, rotate, scale any kind of annotation; Provide enhanced redaction & encryption objects for sensitive information & security;
change pdf document security; convert secure webpage to pdf
CHAPTER 12. THREADS AND MULTIPROCESSING
629
Every time CLMandelbrotMaster is run, it solves exactly the same problem. (For this
demonstration, the nature of the problem is not important, but the problem is to compute the
data needed for a picture of a small piece of the famous “Mandelbrot Set.” If you are interested
in seeing the picture that is produced, uncomment the call to the saveImage() method at the
end of the main() routine inCLMandelbrotMaster.java.)
You can run CLMandelbrotMaster with different numbers of worker programs to see how
the time required to solve the problem depends on the number of workers. (Note that the
worker programs continue to run after the master program exists, so you can run the mas-
ter program several times without having to restart the workers.) In addition, if you run
CLMandelbrotMaster with no command line arguments, it will solve the entire problem on its
own, so you can see how long it takes to do so without using distributed computing. In a trial
that I ran on some rather old, slow computers, it took 40 seconds for CLMandelbrotMaster
to solve the problem on its own. Using just one worker, it took 43 seconds. The extra time
represents extra work involved in using the network; it takes time to set up a network connec-
tion and to send messages over the network. Using two workers (on different computers), the
problem was solved in 22 seconds. In this case, each worker did about half of the work, and
their computations were performed in parallel, so that the job was done in about half the time.
With larger numbers of workers, the time continued to decrease, but only up to a point. The
master program itself has a certain amount of work to do, no matter how many workers there
are, and the total time to solve the problem can never be less than the time it takes for the
master program to do its part. In this case, the minimum time seemed to be about five seconds.
∗ ∗ ∗
Let’s take a look at how this distributed application is programmed. The master program
divides the overall problem into a set of tasks. Each task is represented by an object of type
CLMandelbrotTask. These tasks have to be communicated to the worker programs, and the
worker programs must send back their results. Some protocol is needed for this communication.
Idecided to use character streams. The master encodes a task as a line of text, which is sent
to a worker. The worker decodes the text (into an object of type CLMandelbrotTask) to find
out what task it is supposed to perform. It performs the assigned task. It encodes the results
as another line of text, which it sends back to the master program. Finally, the master decodes
the results and combines them with the results from other tasks. After all the tasks have been
completed and their results have been combined, the problem has been solved.
Aworker receives not just one task, but a sequence of tasks. Each time it finishes a task
and sends back the result, it is assigned a new task. After all tasks are complete, the worker
receives a “close” command that tells it to close the connection. InCLMandelbrotWorker.java,
all this is done in a method named handleConnection() that is called to handle a connection
that has already been opened to the master program. It uses a method readTask() to decode
atask that it receives from the master and a method writeResults() to encode the results of
the task for transmission back to the master. It must also handle any errors that occur:
private static void handleConnection(Socket connection) {
try {
BufferedReader in = new BufferedReader(
new InputStreamReader( connection.getInputStream()) );
PrintWriter out = new PrintWriter(connection.getOutputStream());
while (true) {
String line = in.readLine(); // Message from the master.
if (line == null) {
// End-of-stream encountered -- should not happen.
BMP to JPEG Converter | Convert Bitmap to JPEG, Convert JPEG to
to JPEG Converter provides flexible image processing related options to resize JPEG conversion; Add morphing effects, watermarks to images to protect security;
pdf security; decrypt pdf file
DICOM to PDF Converter | Convert DICOM to PDF, Convert PDF to
Start DICOM - PDF image conversion through drag &drop method; Security protection provided with comment, logo, date or watermarks added to files;
creating a secure pdf document; decrypt pdf password online
CHAPTER 12. THREADS AND MULTIPROCESSING
630
throw new Exception("Connection closed unexpectedly.");
}
if (line.startsWith(CLOSE
CONNECTION
COMMAND)) {
// Represents the normal termination of the connection.
System.out.println("Received close command.");
break;
}
else if (line.startsWith(TASK
COMMAND)) {
// Represents a CLMandelbrotTask that this worker is
// supposed to perform.
CLMandelbrotTask task = readTask(line); // Decode the message.
task.compute(); // Perform the task.
out.println(writeResults(task)); // Send back the results.
out.flush(); // Make sure data is sent promptly!
}
else {
// No other messages are part of the protocol.
throw new Exception("Illegal command received.");
}
}
}
catch (Exception e) {
System.out.println("Client connection closed with error " + e);
}
finally {
try {
connection.close(); // Make sure the socket is closed.
}
catch (Exception e) {
}
}
}
Note that this method is not executed in a separate thread. The worker has only one thing to
do at a time and does not need to be multithreaded.
Turning to the master program,CLMandelbrotMaster.java, we encounter a more complex
situation. The master program must communicate with several workers over several network
connections. To accomplish this, the master program is multi-threaded, with one thread to
manage communication with each worker. A pseudocode outline of the main() routine is quite
simple:
create a list of all tasks that must be performed
if there are no command line arguments {
// The master program does all the tasks itself.
Perform each task.
}
else {
// The tasks will be performed by worker programs.
for each command line argument:
Get information about a worker from command line argument.
Create and start a thread to communicate with the worker.
Wait for all threads to terminate.
}
// All tasks are now complete (assuming no error occurred).
C# Image: Run RasterEdge Web Viewer Sample Project
Right-click the correspond site-> Edit Permissions -> Security -> Group or user names to provide powerful & profession imaging controls, PDF document, image to
pdf secure; pdf security remover
JPEG2000 to JBIG2 Converter | Convert JPEG2000 to JBIG2, Convert
or batch conversion method; Convert GIF & JBIG2 image with morphing effects, watermarks added to protect security; Can be used as
copy text from locked pdf; pdf security
CHAPTER 12. THREADS AND MULTIPROCESSING
631
The list of tasks is stored in a variable, tasks, of type ConcurrentBlock-
ingQueue<CLMandelbrotTask>, tasks. (See Subsection 12.3.2.) The communication threads
take tasks from this list and send them to worker programs. The method tasks.poll() is used
to remove a task from the queue. If the queue is empty, it returns null, which acts as a signal
that all tasks have been assigned and the communication thread can terminate.
The job of a thread is to send a sequence of tasks to a worker thread and to receive the
results that the worker sends back. The thread is also responsible for opening the connection
in the first place. A pseudocode outline for the process executed by the thread might look like:
Create a socket connected to the worker program.
Create input and output streams for communicating with the worker.
while (true) {
Let task = tasks.poll().
If task == null
break; // All tasks have been assigned.
Encode the task into a message and transmit it to the worker.
Read the response from the worker.
Decode and process the response.
}
Send a "close" command to the worker.
Close the socket.
This would work OK. However, there are a few subtle points. First of all, the thread must be
ready to deal witha network error. For example, aworker might shut downunexpectedly. But if
that happens, the master program can continue, provided other workers are still available. (You
can try this when you run the program: Stop one of the worker programs, with CONTROL-C,
and observe that the master program still completes successfully.) A difficulty arises if an
error occurs while the thread is working on a task: If the problem as a whole is going to
be completed, that task will have to be reassigned to another worker. I take care of this by
putting the uncompleted task back into the task list. (Unfortunately, my program does not
handle all possible errors. If the last worker thread fails, there will be no one left to take
over the uncompleted task. Also, if a network connection “hangs” indefinitely without actually
generating an error, my program will also hang, waiting for a response from a worker that
will never arrive. A more robust program would have some way of detecting the problem and
reassigning the task.)
Another defect in the procedure outlined above is that it leaves the worker program idle
while the thread is processing the worker’s response. It would be nice to get a new task to the
worker before processing the response from the previous task. This would keep the worker busy
and allow two operations to proceed simultaneously instead of sequentially. (In this example,
the time it takes to process a response is so short that keeping the worker waiting while it is
done probably makes no significant difference. But as a general principle, it’s desirable to have
as much parallelism as possible in the algorithm.) We can modify the procedure to take this
into account:
try {
Create a socket connected to the worker program.
Create input and output streams for communicating with the worker.
Let currentTask = tasks.poll().
Encode currentTask into a message and send it to the worker.
while (true) {
Read the response from the worker.
GIF to JBIG2 Converter | Convert GIF to JBIG2, Convert JBIG2 to
brightness and more; Convert GIF & JBIG2 image with morphing effects, watermarks added to protect security; Save original images &
decrypt pdf with password; pdf secure signature
CHAPTER 12. THREADS AND MULTIPROCESSING
632
Let nextTask = tasks.poll().
If nextTask != null {
// Send nextTask to the worker before processing the
// response to currentTask.
Encode nextTask into a message and send it to the worker.
}
Decode and process the response to currentTask.
currentTask = nextTask.
if (currentTask == null)
break; // All tasks have been assigned.
}
Send a "close" command to the worker.
Close the socket.
}
catch (Exception e) {
Put uncompleted task, if any, back into the task queue.
}
finally {
Close the connection.
}
Finally, here is how this translates into Java. The pseudocode presented above becomes the
run() method in the class that defines the communication threads used by the master program:
/**
* This class represents one worker thread. The job of a worker thread
* is to send out tasks to a CLMandelbrotWorker program over a network
* connection, and to get back the results computed by that program.
*/
private static class WorkerConnection extends Thread {
int id;
// Identifies this thread in output statements.
String host;
// The host to which this thread will connect.
int port;
// The port number to which this thread will connect.
/**
* The constructor just sets the values of the instance
* variables id, host, and port and starts the thread.
*/
WorkerConnection(int id, String host, int port) {
this.id = id;
this.host = host;
this.port = port;
start();
}
/**
* The run() method of the thread opens a connection to the host and
* port specified in the constructor, then sends tasks to the
* CLMandelbrotWorker program on the other side of that connection.
* If the thread terminates normally, it outputs the number of tasks
* that it processed. If it terminates with an error, it outputs
* an error message.
*/
public void run() {
CHAPTER 12. THREADS AND MULTIPROCESSING
633
int tasksCompleted = 0; // How many tasks has this thread handled.
Socket socket; // The socket for the connection.
try {
socket = new Socket(host,port); // open the connection.
}
catch (Exception e) {
System.out.println("Thread " + id +
" could not open connection to " + host + ":" + port);
System.out.println("
Error: " + e);
return;
}
CLMandelbrotTask currentTask = null;
CLMandelbrotTask nextTask = null;
try {
PrintWriter out = new PrintWriter(socket.getOutputStream());
BufferedReader in = new BufferedReader(
new InputStreamReader(socket.getInputStream()) );
currentTask = tasks.poll();
if (currentTask != null) {
// Send first task to the worker program.
String taskString = writeTask(currentTask);
out.println(taskString);
out.flush();
}
while (currentTask != null) {
String resultString = in.readLine(); // Get results for currentTask.
if (resultString == null)
throw new IOException("Connection closed unexpectedly.");
if (! resultString.startsWith(RESULT
COMMAND))
throw new IOException("Illegal string received from worker.");
nextTask = tasks.poll(); // Get next task and send it to worker.
if (nextTask != null) {
// Send nextTask to worker before processing results for
// currentTask, so that the worker can work on nextTask
// while the currentTask results are processed.
String taskString = writeTask(nextTask);
out.println(taskString);
out.flush();
}
readResults(resultString, currentTask);
finishTask(currentTask); // Process results from currentTask.
tasksCompleted++;
currentTask = nextTask;
// We are finished with old currentTask.
nextTask = null;
}
out.println(CLOSE
CONNECTION
COMMAND); // Send close command to worker.
out.flush();
}
catch (Exception e) {
System.out.println("Thread " + id + " terminated because of an error");
System.out.println("
Error: " + e);
e.printStackTrace();
CHAPTER 12. THREADS AND MULTIPROCESSING
634
// Put uncompleted tasks, if any, back into the task list.
if (currentTask != null)
reassignTask(currentTask);
if (nextTask != null)
reassignTask(nextTask);
}
finally {
System.out.println("Thread " + id + " ending after completing " +
tasksCompleted + " tasks");
try {
socket.close();
}
catch (Exception e) {
}
}
} //end run()
} // end nested class WorkerConnection
12.5 Network Programming Example:
ANetworked Game Framework
T
his section presents several programs that use networking and threads. The common
(online)
problem in each application is to support network communication between several programs
running on different computers. A typical example of such an application is a networked game
with two or more players, but the same problem can come up in less frivolous applications as
well. The first part of this section describes a common framework that can be used for a variety
of such applications, and the rest of the section discusses three specific applications that use
that framework.
This section was inspired by a pair of students, Alexander Kittelberger and Kieran Koehn-
lein, who wanted to write a networked poker game as a final project in a class that I was
teaching. I helped them with the network part of the project by writing a basic framework
to support communication between the players. Since the application illustrates a variety of
important ideas, I decided to include a somewhat more advanced and general version of that
framework in the current edition of this book. The final example is a networked poker game.
12.5.1 The Netgame Framework
One can imagine playing many different games over the network. As far as the network goes,
all of those games have at least one thing in common: There has to be some way for actions
taken by one player to be communicated over the network to other players. It makes good
programming sense to make that capability available in a reusable common core that can be
used in many different games. I have written such a core; it is defined by several classes in the
packagenetgame.common.
We have not done much with packages in this book, aside from using built-in classes. Pack-
ages were introduced inSubsection2.6.4, but we have stuck to the “default package” in our
programming examples. In practice, however, packages are used in all but the simplest pro-
gramming projects to divide the code into groups of related classes. It makes particularly good
CHAPTER 12. THREADS AND MULTIPROCESSING
635
sense to define a reusable framework in a package that can be included as a unit in a variety of
projects.
Integrated development environments such as Eclipse or Netbeans make it very easy to use
packages: To use the netgame package in a project in an IDE, simply copy-and-paste the entire
netgamedirectoryintotheproject.
If you work on the command line, you should be in a working directory that includes
the netgame directory as a subdirectory. Then, to compile all the java files in the package
netgame.common,forexample,youcanusethefollowingcommandinMacOSorLinux:
javac netgame/common/*.java
For windows, you should use backslashes instead of forward slashes:
javac netgame\common\*.java
To run a main program that is defined in a package, you should again be in a directory that
contains the package as a subdirectory, and you should use the full name of the class that you
want to run. For example, the ChatRoomServer class, discussed later in this section, is defined
in the packagenetgame.chat, so you would run it with the command
java netgame.chat.ChatRoomServer
Iwill have more to say about packages in the final example of the book, inSection13.5.
∗ ∗ ∗
The applications discussed in this section are examples of distributed computing, since they
involveseveral computers communicatingover anetwork. Like the example inSubsection12.4.5,
they use a central “server,” or “master,” to which a number of “clients” will connect. All
communication goes through the server; a client cannot send messages directly to another
client. In this section, I will refer to the server as a hub, in the sense of “communications hub”:
HUB
Client
Client
Client
Client
InSubsection12.4.5, messages were sent back and forth between the server and the client in a
definite, predetermined sequence. Communication between the server and a client was actually
communication between one thread running on the server and another thread running on the
client. For the netgame framework, however, I want to allow for asynchronous communication,
in which it is not possible to wait for messages to arrive in a predictable sequence. To make this
possible a netgame client will use two threads for communication, one for sending messages and
one for receiving messages. Similarly, the netgame hub will use two threads for communicating
with each client.
CHAPTER 12. THREADS AND MULTIPROCESSING
636
The hub is generally connected to many clients and can receive messages from any of those
clients at any time. The hub will have to process each message in some way. To organize this
processing, the hub uses a single thread to process all incoming messages. When a communi-
cation thread receives a message from a client, it simply drops that message into a queue of
incoming messages. There is only one such queue, which is used for messages from all clients.
The message processing thread runs in a loop in which it removes a message from the queue,
processes it, removes another message from the queue, processes it, and so on. The queue itself
is implemented as an object of type LinkedBlockingQueue (seeSubsection12.3.3).
send  thread
receive  thread
Client
send
receive
HUB
Client
Incoming
Messages Queue
Message
Processing
Thread
There is one more thread in the hub, not shown in the illustration. This final thread
creates a ServerSocket and uses it to listen for connection requests from clients. Each time it
accepts a connection request, it hands off the client to another object, defined by the nested
class ConnectionToClient, which will handle communication with that client. Each connected
client is identified by an ID number. ID numbers 1, 2, 3, ... are assigned to clients as they
connect. Since clients can also disconnect, the clients connected at any give time might not
have consecutive IDs. A variable of type TreeMap<Integer,ConnectionToClient> associates the
ID numbers of connected clients with the objects that handle their connections.
The messages that are sent and received are objects. The I/O streams that are used for
reading and writing objects are of type ObjectInputStream and ObjectOutputStream. (SeeSub-
section 11.1.6.) TheoutputstreamofasocketiswrappedinanObjectOutputStreamtomake
it possible to transmit objects through that socket. The socket’s input stream is wrapped in
an ObjectInputStream to make it possible to receive objects. Remember that the objects that
are used with such streams must implement the interface java.io.Serializable.
The netgame Hub class is defined in the file Hub.java, in the packagenetgame.common.
The port on which the server socket will listen must be specified as a parameter to the Hub
constructor. The Hub class defines a method
protected void messageReceived(int playerID, Object message)
which is called to process messages that are received from clients. The first parameter,
playerID, is the ID number of the client from whom the message was received, and the second
parameter is the message itself. In the Hub class, this method will simply forward the mes-
sage to all connected clients. To forward the message, it wraps both the playerID and the
message in an object of type ForwardedMessage (defined in the fileForwardedMessage.java, in
the package netgame.common). In a simple application such as the chat room discussed in the
next subsection, this might be sufficient. For most applications, however, it will be necessary to
Documents you may be interested
Documents you may be interested