Download Multithreading and more Schemes and Mind Maps Java Programming in PDF only on Docsity!
Multithreading
WHAT ARE THREADS?
INTERRUPTING THREADS
THREAD PROPERTIES
THREAD PRIORITIES
SELFISH THREADS
SYNCHRONIZATION
DEADLOCKS
USER INTERFACE PROGRAMMING WITH THREADS
USING PIPES FOR COMMUNICATION BETWEEN THREADS
You are probably familiar with multitasking: the ability to have more than
one program working at what seems like the same time. For example, you can
print while editing or sending a fax. Of course, unless you have a multiple-proces-
sor machine, what is really going on is that the operating system is doling out
resources to each program, giving the impression of parallel activity. This
resource distribution is possible because while you may think you are keeping the
computer busy by, for example, entering data, most of the CPU’s time will be idle.
(A fast typist takes around 1/20 of a second per character typed, after all, which is
a huge time interval for a computer.)
Multitasking can be done in two ways, depending on whether the operating sys-
tem interrupts programs without consulting with them first, or whether pro-
grams are only interrupted when they are willing to yield control. The former is
called preemptive multitasking; the latter is called cooperative (or, simply, nonpre-
emptive) multitasking. Windows 3.1 and Mac OS 9 are cooperative multitasking
systems, and UNIX/Linux, Windows NT (and Windows 95 for 32-bit programs),
2 Core Java
and OS X are preemptive. (Although harder to implement, preemptive multitask-
ing is much more effective. With cooperative multitasking, a badly behaved
program can hog everything.)
Multithreaded programs extend the idea of multitasking by taking it one level
lower: individual programs will appear to do multiple tasks at the same time.
Each task is usually called a thread— which is short for thread of control. Programs
that can run more than one thread at once are said to be multithreaded. Think of
each thread as running in a separate context: contexts make it seem as though
each thread has its own CPU—with registers, memory, and its own code.
So, what is the difference between multiple processes and multiple threads? The essen-
tial difference is that while each process has a complete set of its own variables,
threads share the same data. This sounds somewhat risky, and indeed it can be, as
you will see later in this chapter. But it takes much less overhead to create and destroy
individual threads than it does to launch new processes, which is why all modern
operating systems support multithreading. Moreover, inter-process communication
is much slower and more restrictive than communication between threads.
Multithreading is extremely useful in practice. For example, a browser should be
able to simultaneously download multiple images. An email program should let
you read your email while it is downloading new messages. The Java program-
ming language itself uses a thread to do garbage collection in the background—
thus saving you the trouble of managing memory! Graphical user interface (GUI)
programs have a separate thread for gathering user interface events from the host
operating environment. This chapter shows you how to add multithreading capa-
bility to your Java applications and applets.
Fair warning: multithreading can get very complex. In this chapter, we present
all of the tools that the Java programming language provides for thread pro-
gramming. We explain their use and limitations and give some simple but typ-
ical examples. However, for more intricate situations, we suggest that you
turn to a more advanced reference, such as Concurrent Programming in Java by
Doug Lea [Addison-Wesley 1999].
NOTE: In many programming languages, you have to use an external thread package to do multithreaded programming. The Java programming language builds in multi- threading, which makes your job much easier.
What Are Threads?
Let us start by looking at a program that does not use multiple threads and
that, as a consequence, makes it difficult for the user to perform several tasks
with that program. After we dissect it, we will then show you how easy it is
4 Core Java
The call to Thread.sleep does not create a new thread—sleep is a static method
of the Thread class that temporarily stops the activity of the current thread.
The sleep method can throw an InterruptedException. We will discuss this
exception and its proper handling later. For now, we simply terminate the bounc-
ing if this exception occurs.
If you run the program, the ball bounces around nicely, but it completely takes over
the application. If you become tired of the bouncing ball before it has finished its
1,000 bounces and click on the “Close” button, the ball continues bouncing anyway.
You cannot interact with the program until the ball has finished bouncing.
NOTE: If you carefully look over the code at the end of this section, you will notice the call canvas.paint(canvas.getGraphics()) inside the move method of the Ball class. That is pretty strange—normally, you’d call repaint and let the AWT worry about getting the graphics context and doing the painting. But if you try to call canvas.repaint() in this program, you’ll find out that the canvas is never repainted since the addBall method has completely taken over all processing. In the next program, where we use a separate thread to compute the ball position, we’ll again use the familiar repaint.
Obviously, the behavior of this program is rather poor. You would not want
the programs that you use behaving in this way when you ask them to do a
time-consuming job. After all, when you are reading data over a network con-
nection, it is all too common to be stuck in a task that you would really like to
interrupt. For example, suppose you download a large image and decide, after
seeing a piece of it, that you do not need or want to see the rest; you certainly
would like to be able to click on a “Stop” or “Back” button to interrupt the
loading process. In the next section, we will show you how to keep the user in
control by running crucial parts of the code in a separate thread.
Example 1–1 is the entire code for the program.
Example 1–1: Bounce.java
- import java.awt.*;
- import java.awt.event.*;
- (^) import java.awt.geom.*;
- import java.util.*;
- import javax.swing.*;
- /**
- Shows an animated bouncing ball.
- */
1 • Multithreading 5
- public class Bounce
- {
- public static void main(String[] args)
- {
- JFrame frame = new BounceFrame();
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.show();
- }
- }
- (^) /**
- The frame with canvas and buttons.
- */
- class BounceFrame extends JFrame
- {
- /**
- Constructs the frame with the canvas for showing the
- bouncing ball and Start and Close buttons
- */
- public BounceFrame()
- {
- setSize(WIDTH, HEIGHT);
- (^) setTitle("Bounce");
- Container contentPane = getContentPane();
- canvas = new BallCanvas();
- contentPane.add(canvas, BorderLayout.CENTER);
- JPanel buttonPanel = new JPanel();
- addButton(buttonPanel, "Start",
- new ActionListener()
- {
- public void actionPerformed(ActionEvent evt)
- {
- addBall();
- }
- });
- addButton(buttonPanel, "Close",
- new ActionListener()
- {
- public void actionPerformed(ActionEvent evt)
- {
- System.exit(0);
- }
- });
- contentPane.add(buttonPanel, BorderLayout.SOUTH);
- }
- /**
- Adds a button to a container.
- @param c the container
1 • Multithreading 7
- public void paintComponent(Graphics g)
- {
- super.paintComponent(g);
- Graphics2D g2 = (Graphics2D)g;
- for (int i = 0; i < balls.size(); i++)
- {
- Ball b = (Ball)balls.get(i);
- b.draw(g2);
- }
- }
- private ArrayList balls = new ArrayList();
- }
- /**
- A ball that moves and bounces off the edges of a
- component
- */
- class Ball
- {
- /**
- Constructs a ball in the upper left corner
- @c the component in which the ball bounces
- */
- public Ball(Component c) { canvas = c; }
- /**
- Draws the ball at its current position
- @param g2 the graphics context
- */
- public void draw(Graphics2D g2)
- {
- g2.fill(new Ellipse2D.Double(x, y, XSIZE, YSIZE));
- }
- /**
- Moves the ball to the next position, reversing direction
- if it hits one of the edges
- */
- public void move()
- {
- x += dx;
- y += dy;
- if (x < 0)
- {
- x = 0;
- dx = -dx;
- }
- if (x + XSIZE >= canvas.getWidth())
- {
- x = canvas.getWidth() - XSIZE;
- dx = -dx;
8 Core Java
- if (y < 0)
- {
- y = 0;
- dy = -dy;
- }
- if (y + YSIZE >= canvas.getHeight())
- {
- y = canvas.getHeight() - YSIZE;
- dy = -dy;
- }
- canvas.paint(canvas.getGraphics());
- }
- private Component canvas;
- private static final int XSIZE = 15;
- private static final int YSIZE = 15;
- private int x = 0;
- private int y = 0;
- private int dx = 2;
- private int dy = 2;
- }
- static void sleep(long millis)
sleeps for the given number of milliseconds
In the previous sections, you learned what is required to split a program into
multiple concurrent tasks. Each task needs to be placed into a run method of
a class that extends Thread. But what if we want to add the run method to a
class that already extends another class? This occurs most often when we
want to add multithreading to an applet. An applet class already inherits
from JApplet, and we cannot inherit from two parent classes, so we need to
use an interface. The necessary interface is built into the Java platform. It is
called Runnable. We take up this important interface next.
Using Threads to Give Other Tasks a Chance
We will make our bouncing-ball program more responsive by running the code
that moves the ball in a separate thread.
NOTE: Since most computers do not have multiple processors, the Java virtual machine (JVM) uses a mechanism in which each thread gets a chance to run for a little while, then activates another thread. The virtual machine generally relies on the host operating sys- tem to provide the thread scheduling package.
java.lang.Thread
Parameters: millis the number of milliseconds to sleep
10 Core Java
CAUTION: Do not call the run method directly—start will call it when the thread is set up and ready to go. Calling the run method directly merely executes its contents in the same thread—no new thread is started.
Beginners are sometimes misled into believing that every method of a Thread
object automatically runs in a new thread. As you have seen, that is not true. The
methods of any object (whether a Thread object or not) run in whatever thread
they are called. A new thread is only started by the start method. That new
thread then executes the run method.
In the Java programming language, a thread needs to tell the other threads when it is
idle, so the other threads can grab the chance to execute the code in their run proce-
dures. (See Figure 1–2.) The usual way to do this is through the static sleep method.
The run method of the BallThread class uses the call to sleep(5) to indicate that
the thread will be idle for the next five milliseconds. After five milliseconds, it will
start up again, but in the meantime, other threads have a chance to get work done.
TIP: There are a number of static methods in the Thread class. They all operate on the current thread, that is, the thread that executes the method. For example, the static sleep method idles the thread that is calling sleep.
Figure 1–2: The Event Dispatch and Ball Threads
Event dispatch thread
Ball thread
move
sleep
move
sleep
start
1 • Multithreading 11
The complete code is shown in Example 1–2.
Example 1–2: BounceThread.java
- import java.awt.*;
- import java.awt.event.*;
- import java.awt.geom.*;
- import java.util.*;
- import javax.swing.*;
- /**
- Shows an animated bouncing ball running in a separate thread
- */
- public class BounceThread
- {
- public static void main(String[] args)
- {
- JFrame frame = new BounceFrame();
- frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- frame.show();
- }
- }
- /**
- The frame with canvas and buttons.
- */
- class BounceFrame extends JFrame
- {
- /**
- Constructs the frame with the canvas for showing the
- bouncing ball and Start and Close buttons
- */
- public BounceFrame()
- {
- setSize(WIDTH, HEIGHT);
- setTitle("BounceThread");
- Container contentPane = getContentPane();
- canvas = new BallCanvas();
- contentPane.add(canvas, BorderLayout.CENTER);
- JPanel buttonPanel = new JPanel();
- addButton(buttonPanel, "Start",
- new ActionListener()
- {
- public void actionPerformed(ActionEvent evt)
- {
- addBall();
1 • Multithreading 13
- class BallThread extends Thread
- {
- /**
- Constructs the thread.
- @aBall the ball to bounce
- */
- public BallThread(Ball aBall) { b = aBall; }
- public void run()
- {
- try
- {
- for (int i = 1; i <= 1000; i++)
- {
- b.move();
- sleep(5);
- }
- }
- catch (InterruptedException exception)
- {
- }
- }
- private Ball b;
- }
- /**
- The canvas that draws the balls.
- */
- class BallCanvas extends JPanel
- {
- /**
- Add a ball to the canvas.
- @param b the ball to add
- */
- public void add(Ball b)
- {
- balls.add(b);
- }
- public void paintComponent(Graphics g)
- {
- super.paintComponent(g);
- Graphics2D g2 = (Graphics2D)g;
- for (int i = 0; i < balls.size(); i++)
- {
14 Core Java
- Ball b = (Ball)balls.get(i);
- b.draw(g2);
- }
- }
- private ArrayList balls = new ArrayList();
- }
- /**
- A ball that moves and bounces off the edges of a
- component
- */
- class Ball
- {
- /**
- Constructs a ball in the upper left corner
- @c the component in which the ball bounces
- */
- public Ball(Component c) { canvas = c; }
- /**
- Draws the ball at its current position
- @param g2 the graphics context
- */
- public void draw(Graphics2D g2)
- {
- g2.fill(new Ellipse2D.Double(x, y, XSIZE, YSIZE));
- }
- /**
- Moves the ball to the next position, reversing direction
- if it hits one of the edges
- */
- public void move()
- {
- x += dx;
- y += dy;
- if (x < 0)
- {
- x = 0;
- dx = -dx;
- }
- if (x + XSIZE >= canvas.getWidth())
- {
- x = canvas.getWidth() - XSIZE;
- dx = -dx;
- }
16 Core Java
Figure 1–3: Multiple threads
This example demonstrates a great advantage of the thread architecture in the
Java programming language. It is very easy to create any number of autonomous
objects that appear to run in parallel.
Occasionally, you may want to enumerate the currently running threads—see the
API note in the “Thread Groups” section for details.
The Runnable Interface
We could have saved ourselves a class by having the Ball class extend the
Thread class. As an added advantage of that approach, the run method has
access to the private fields of the Ball class:
class Ball extends Thread { public void run() { try { for (int i = 1; i <= 1000; i++) { x += dx; y += dy;
... canvas.repaint(); sleep(5); } } catch (InterruptedException exception)
1 • Multithreading 17
private Component canvas; private int x = 0; private int y = 0; private int dx = 2; private int dy = 2; }
Conceptually, of course, this is dubious. A ball isn’t a thread, so inheritance isn’t really
appropriate. Nevertheless, programmers sometimes follow this approach when the
run method of a thread needs to access private fields of another class. In the preced-
ing section, we’ve avoided that issue altogether by having the run method call only
public methods of the Ball class, but it isn’t always so easy to do that.
Suppose the run method needs access to private fields, but the class into which
you want to put the run method already has another superclass. Then it can’t
extend the Thread class, but you can make the class implement the Runnable
interface. As though you had derived from Thread, put the code that needs to
run in the run method. For example,
class Animation extends JApplet implements Runnable {
... public void run() { // thread action goes here } }
You still need to make a thread object to launch the thread. Give that thread a ref-
erence to the Runnable object in its constructor. The thread then calls the run
method of that object.
class Animation extends JApplet implements Runnable {
... public void start() { runner = new Thread(this); runner.start(); } ... private Thread runner; }
1 • Multithreading 19
There is no longer a way to force a thread to terminate. However, the
interrupt method can be used to request termination of a thread. That
means that the run method of a thread ought to check once in a while
whether it should exit.
public void run() {
... while ( no request to terminate && more work to do ) { do more work } // exit run method and terminate thread }
However, as you have learned, a thread should not work continuously, but it
should go to sleep or wait once in a while, to give other threads a chance to
do their work. But when a thread is sleeping, it can’t actively check whether
it should terminate. This is where the InterruptedException comes in.
When the interrupt method is called on a thread object that is currently
blocked, the blocking call (such as sleep or wait) is terminated by an
InterruptedException.
There is no language requirement that a thread that is interrupted should
terminate. Interrupting a thread simply grabs its attention. The interrupted
thread can decide how to react to the interruption by placing appropriate
actions into the catch clause that deals with the InterruptedException.
Some threads are so important that they should simply ignore their interrup-
tion by catching the exception and continuing. But quite commonly, a thread
will simply want to interpret an interruption as a request for termination.
The run method of such a thread has the following form:
public void run() { try {
... while ( more work to do ) { do more work } } catch(InterruptedException exception) { // thread was interrupted during sleep or wait } finally
20 Core Java
cleanup, if required } // exit run method and terminate thread }
However, there is a problem with this code skeleton. If the interrupt
method was called while the thread was not sleeping or waiting, then no
InterruptedException was generated. The thread needs to call the
interrupted method to find out if it was recently interrupted.
while ( !interrupted() && more work to do ) { do more work }
In particular, if a thread was blocked while waiting for input/output, the
input/output operations are not terminated by the call to interrupt. When
the blocking operation has returned, you need to call the interrupted
method to find out if the current thread has been interrupted.
NOTE: Curiously, there are two very similar methods, interrupted and isInterrupted. The interrupted method is a static method that checks whether the current thread has been interrupted. (Recall that a thread is inter- rupted because another thread has called its interrupt method.) Further- more, calling the interrupted method resets the “interrupted” status of the thread. On the other hand, the isInterrupted method is an instance method that you can use to check whether any thread has been interrupted. Calling it does not change the “interrupted” status of its argument.
It is a bit tedious that there are two distinct ways of dealing with
thread interruption—testing the “interrupted” flag and catching the
InterruptedException.
It would have been nice if methods such as sleep had been defined to
simply return with the “interrupted” flag set when an interruption occurs—
then one wouldn’t have to deal with the InterruptedException at all.
Of course, you can manually set the “interrupted” flag when an
InterruptedException is caught:
try { sleep(delay); } catch (InterruptedException exception) {