- •Contents
- •Preface
- •Introduction to Computers, the Internet and the Web
- •1.3 Computer Organization
- •Languages
- •1.9 Java Class Libraries
- •1.12 The Internet and the World Wide Web
- •1.14 General Notes about Java and This Book
- •Sections
- •Introduction to Java Applications
- •2.4 Displaying Text in a Dialog Box
- •2.5 Another Java Application: Adding Integers
- •2.8 Decision Making: Equality and Relational Operators
- •Introduction to Java Applets
- •3.2 Sample Applets from the Java 2 Software Development Kit
- •3.3 A Simple Java Applet: Drawing a String
- •3.4 Two More Simple Applets: Drawing Strings and Lines
- •3.6 Viewing Applets in a Web Browser
- •3.7 Java Applet Internet and World Wide Web Resources
- •Repetition)
- •Class Attributes
- •5.8 Labeled break and continue Statements
- •5.9 Logical Operators
- •Methods
- •6.2 Program Modules in Java
- •6.7 Java API Packages
- •6.13 Example Using Recursion: The Fibonacci Series
- •6.16 Methods of Class JApplet
- •Class Operations
- •Arrays
- •7.6 Passing Arrays to Methods
- •7.8 Searching Arrays: Linear Search and Binary Search
- •Collaboration Among Objects
- •8.2 Implementing a Time Abstract Data Type with a Class
- •8.3 Class Scope
- •8.4 Controlling Access to Members
- •8.5 Creating Packages
- •8.7 Using Overloaded Constructors
- •8.9 Software Reusability
- •8.10 Final Instance Variables
- •Classes
- •8.16 Data Abstraction and Encapsulation
- •9.2 Superclasses and Subclasses
- •9.5 Constructors and Finalizers in Subclasses
- •Conversion
- •9.11 Type Fields and switch Statements
- •9.14 Abstract Superclasses and Concrete Classes
- •9.17 New Classes and Dynamic Binding
- •9.18 Case Study: Inheriting Interface and Implementation
- •9.19 Case Study: Creating and Using Interfaces
- •9.21 Notes on Inner Class Definitions
- •Strings and Characters
- •10.2 Fundamentals of Characters and Strings
- •10.21 Card Shuffling and Dealing Simulation
- •Handling
- •Graphics and Java2D
- •11.2 Graphics Contexts and Graphics Objects
- •11.5 Drawing Lines, Rectangles and Ovals
- •11.9 Java2D Shapes
- •12.12 Adapter Classes
- •Cases
- •13.3 Creating a Customized Subclass of JPanel
- •Applications
- •Controller
- •Exception Handling
- •14.6 Throwing an Exception
- •14.7 Catching an Exception
- •Multithreading
- •15.3 Thread States: Life Cycle of a Thread
- •15.4 Thread Priorities and Thread Scheduling
- •15.5 Thread Synchronization
- •15.9 Daemon Threads
- •Multithreading
- •Design Patterns
- •Files and Streams
- •16.2 Data Hierarchy
- •16.3 Files and Streams
- •Networking
- •17.2 Manipulating URIs
- •17.3 Reading a File on a Web Server
- •17.4 Establishing a Simple Server Using Stream Sockets
- •17.5 Establishing a Simple Client Using Stream Sockets
- •17.9 Security and the Network
- •18.2 Loading, Displaying and Scaling Images
- •18.3 Animating a Series of Images
- •18.5 Image Maps
- •18.6 Loading and Playing Audio Clips
- •18.7 Internet and World Wide Web Resources
- •Data Structures
- •19.4 Linked Lists
- •20.8 Bit Manipulation and the Bitwise Operators
- •Collections
- •21.8 Maps
- •21.9 Synchronization Wrappers
- •21.10 Unmodifiable Wrappers
- •22.2 Playing Media
- •22.3 Formatting and Saving Captured Media
- •22.5 Java Sound
- •22.8 Internet and World Wide Web Resources
- •Hexadecimal Numbers
Chapter 15 |
Multithreading |
869 |
48
49 } // end class SharedCell
Fig. 15.16 Threads modifying a shared array of cells (part 2 of 2).
15.9 Daemon Threads
A daemon thread is a thread that runs for the benefit of other threads. Unlike conventional user threads (i.e., any non-daemon thread in a program), daemon threads do not prevent a program from terminating. The garbage collector is a daemon thread. Nondaemon threads are conventional user threads or threads such as the event-dispatch thread used to process GUI events. We designate a thread as a daemon with the method call
setDaemon( true );
A false argument means that the thread is not a daemon thread. A program can include a mixture of daemon threads and nondaemon threads. When only daemon threads remain in a program, the program exits. If a thread is to be a daemon, it must be set as such before its start method is called or an IllegalThreadStateException is thrown. Method isDaemon returns true if a thread is a daemon thread and false otherwise.
Common Programming Error 15.4
Starting a thread, then attempting to make the thread a daemon thread, causes an Ille-
galThreadStateException.
870 Multithreading Chapter 15
Software Engineering Observation 15.3
The event-dispatch thread is an infinite loop and is not a daemon thread. As such, the event-
dispatch thread will not terminate in a windowed application until the application calls
System method exit.
Good Programming Practice 15.1
Do not assign critical tasks to a daemon thread. They are terminated without warning, which may prevent those tasks from completing properly.
15.10 Runnable Interface
Until now, we extended class Thread to create new classes that support multithreading. We overrode the run method to specify the tasks to be performed concurrently. However, if we want multithreading support in a class that already extends a class other than Thread, we must implement the Runnable interface in that class, because Java does not allow a class to extend more than one class at a time. Class Thread itself implements the Runnable interface (package java.lang) as expressed in the class header
public class Thread extends Object implements Runnable
Implementing the Runnable interface in a class enables a program to manipulate objects of that class as Runnable objects. As with deriving from the Thread class, the code that controls the thread is placed in method run.
A program that uses a Runnable object creates a Thread object and associates the Runnable object with that Thread. Class Thread provides four constructors that can receive references to Runnable objects as arguments. For example, the constructor
public Thread( Runnable runnableObject )
registers method run of runnableObject as the method to be invoked when the thread begins execution. The constructor
public Thread( Runnable runnableObject, String threadName )
constructs a Thread with the name threadName and registers method run of its runnableObject argument as the method to be invoked when the thread begins execution. As always, the thread object’s start method must be called to begin the thread’s execution.
Figure 15.17 demonstrates an applet with a private inner class and an anonymous inner class that each implement interface Runnable. The example also demonstrates how to suspend a thread (i.e., temporarily prevent it from executing), how to resume a suspended thread and how to terminate a thread that executes until a condition becomes false. Each of these techniques is important because Thread methods suspend, resume and stop were deprecated (i.e., they should no longer be used in Java programs) with the introduction of the Java 2 Platform. Because these methods are deprecated, we must code our own mechanisms for suspending, resuming and stopping threads. As we will demonstrate, these mechanisms rely on synchronized blocks of code, loops and boolean flag variables.
Chapter 15 |
Multithreading |
871 |
The applet class RandomCharacters displays three JLabels and three JCheckBoxes. A separate thread of execution is associated with each JLabel and button pair. Each thread randomly displays letters from the alphabet in its corresponding JLabel object. The applet defines the String alphabet (line 16) containing the letters from A to Z. This string is shared among the three threads. The applet’s start method (lines 52– 65) instantiates three Thread objects (lines 59–60) and initializes each with an instance of class RunnableObject, which implements interface Runnable. Line 63 invokes the Thread class start method on each Thread, placing the threads in the ready state.
Class RunnableObject is defined at lines 115–185. Its run method (line 120) defines two local variables. Line 123 uses static method currentThread of class Thread to determine the currently executing Thread object. Line 125 calls the applet’s utility method getIndex (defined at lines 68–76) to determine the index of the currently executing thread in the array threads. The current thread displays a random character in the JLabel object with the same index in array outputs.
1// Fig. 15.17: RandomCharacters.java
2 // Demonstrating the Runnable interface.
3
4 // Java core packages
5import java.awt.*;
6 import java.awt.event.*;
7
8 // Java extension packages
9 import javax.swing.*;
10
11public class RandomCharacters extends JApplet
12implements ActionListener {
13
14// declare variables used by applet and
15// inner class RunnableObject
16private String alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
17private final static int SIZE = 3;
18
19private JLabel outputs[];
20private JCheckBox checkboxes[];
22private Thread threads[];
23private boolean suspended[];
25// set up GUI and arrays
26public void init()
27{
28outputs = new JLabel[ SIZE ];
29checkboxes = new JCheckBox[ SIZE ];
30threads = new Thread[ SIZE ];
31suspended = new boolean[ SIZE ];
32
33Container container = getContentPane();
34container.setLayout( new GridLayout( SIZE, 2, 5, 5 ) );
Fig. 15.17 Demonstrating the Runnable interface, suspending threads and resuming threads (part 1 of 5).
872 |
Multithreading |
Chapter 15 |
35
36// create GUI components, register listeners and attach
37// components to content pane
38for ( int count = 0; count < SIZE; count++ ) {
39 |
outputs[ count |
] = new JLabel(); |
40 |
outputs[ count |
].setBackground( Color.green ); |
41 |
outputs[ count |
].setOpaque( true ); |
42 |
container.add( |
outputs[ count ] ); |
43 |
|
|
44 |
checkboxes[ count ] = new JCheckBox( "Suspended" ); |
|
45 |
checkboxes[ count ].addActionListener( this ); |
46container.add( checkboxes[ count ] );
47}
48}
49
50// Create and start threads. This method called after init
51// and when user revists Web page containing this applet
52public void start()
53{
54// create threads and start every time start is called
55for ( int count = 0; count < threads.length; count++ ) {
57 |
// create Thread and initialize it with object that |
|
58 |
// implements Runnable |
|
59 |
threads[ count ] = new |
Thread( new RunnableObject(), |
60 |
"Thread " + ( count |
+ 1 ) ); |
61 |
|
|
62 |
// begin executing Thread |
63threads[ count ].start();
64}
65}
66
67// determine thread location in threads array
68private int getIndex( Thread current )
69{
70for ( int count = 0; count < threads.length; count++ )
72 |
if ( current == threads[ count ] ) |
73 |
return count; |
74 |
|
75return -1;
76}
77
78// called when user switches Web pages; stops all threads
79public synchronized void stop()
80{
81// Indicate that each thread should terminate. Setting
82// these references to null causes each thread's run
83// method to complete execution.
84for ( int count = 0; count < threads.length; count++ )
85 |
threads[ count ] = null; |
86 |
|
Fig. 15.17 Demonstrating the Runnable interface, suspending threads and resuming threads (part 2 of 5).
Chapter 15 |
Multithreading |
873 |
87// make all waiting threads ready to execute, so they
88// can terminate themselves
89notifyAll();
90}
91
92// handle button events
93public synchronized void actionPerformed( ActionEvent event )
94{
95for ( int count = 0; count < checkboxes.length; count++ ) {
97 |
if ( event.getSource() == checkboxes[ count ] ) { |
98 |
suspended[ count ] = !suspended[ count ]; |
99 |
|
100 |
// change label color on suspend/resume |
101 |
outputs[ count ].setBackground( |
102 |
!suspended[ count ] ? Color.green : Color.red ); |
103 |
|
104 |
// if thread resumed, make sure it starts executing |
105 |
if ( !suspended[ count ] ) |
106 |
notifyAll(); |
107 |
|
108 |
return; |
109}
110}
111}
112
113// private inner class that implements Runnable so objects
114// of this class can control threads
115private class RunnableObject implements Runnable {
116
117// Place random characters in GUI. Local variables
118// currentThread and index are declared final so
119// they can be used in an anonymous inner class.
120public void run()
121{
122 |
// get reference to executing thread |
123 |
final Thread currentThread = Thread.currentThread(); |
124 |
|
125 |
// determine thread's position in array |
126 |
final int index = getIndex( currentThread ); |
127 |
|
128 |
// loop condition determines when thread should stop |
129 |
while ( threads[ index ] == currentThread ) { |
130 |
|
131 |
// sleep from 0 to 1 second |
132 |
try { |
133 |
Thread.sleep( ( int ) ( Math.random() * 1000 ) ); |
134 |
|
135 |
// Determine whether thread should suspend |
136 |
// execution. Use applet as monitor. |
137 |
synchronized( RandomCharacters.this ) { |
138 |
|
Fig. 15.17 Demonstrating the Runnable interface, suspending threads and resuming threads (part 3 of 5).
874 |
Multithreading |
Chapter 15 |
139 |
|
while ( suspended[ index ] && |
|
|
|
140 |
|
threads[ index ] == currentThread ) { |
141 |
|
|
142 |
|
// Temporarily stop thread execution. Use |
143 |
|
// applet as monitor. |
144 |
|
RandomCharacters.this.wait(); |
145 |
|
} |
146 |
|
|
147 |
} |
// end synchronized block |
148 |
} |
|
149 |
|
|
150 |
// process InterruptedExceptions during sleep or wait |
|
151 |
catch ( InterruptedException interruptedException ) { |
|
152 |
System.err.println( "sleep interrupted" ); |
|
153 |
} |
|
154 |
|
|
155 |
// display character on corresponding label |
|
156 |
SwingUtilities.invokeLater( |
|
157 |
|
|
158 |
// anonymous inner class used by SwingUtilities |
|
159 |
// method invokeLater to ensure GUI |
|
160 |
// updates properly |
|
161 |
new Runnable() { |
|
162 |
|
|
163 |
|
// updates Swing GUI component |
164 |
|
public void run() |
165 |
|
{ |
166 |
|
// pick random character |
167 |
|
char displayChar = alphabet.charAt( |
168 |
|
( int ) ( Math.random() * 26 ) ); |
169 |
|
|
170 |
|
outputs[ index ].setText( |
171 |
|
currentThread.getName() + ": " + |
172 |
|
displayChar ); |
173 |
|
} |
174 |
|
|
175 |
} |
// end anonymous inner class |
176 |
|
|
177 |
); // end call to SwingUtilities.invokeLater |
|
178 |
|
|
179 |
} // end while |
|
180 |
|
|
181 |
System.err.println( |
182currentThread.getName() + " terminating" );
183}
184
185 } // end private inner class RunnableObject
186
187 } // end class RandomCharacters
Fig. 15.17 Demonstrating the Runnable interface, suspending threads and resuming threads (part 4 of 5).
Chapter 15 |
Multithreading |
875 |
|
|
|
|
|
|
Fig. 15.17 Demonstrating the Runnable interface, suspending threads and resuming threads (part 5 of 5).
The while loop at lines 129–179 continues to execute as long as the specified Thread reference is equal to the reference to the currently executing thread (currentThread). In each iteration of the loop, the thread sleeps for a random interval from 0 to 1 second.
When the user clicks the JCheckBox to the right of a particular JLabel, the corresponding Thread should be suspended (temporarily prevented from executing) or resumed (allowed to continue executing). In previous versions of Java, methods suspend and resume of class Thread were provided to suspend and resume a thread’s execution. These methods are now deprecated (i.e., they should no longer be used) because they introduce the possibility of deadlock in a program if they are not used correctly. Suspending and resuming of a thread can be implemented using thread synchronization and methods wait and notify of class Object. Lines 137–147 define a synchronized block of code (also called a synchronized statement) that helps suspend the currently executing Thread. When the Thread reaches the synchronized block, the applet object (referenced with RandomCharacters.this) is locked and the while structure tests suspended[ index ] to determine if the Thread should be suspended (i.e., true). If so, line 144 invokes method wait on the applet object to place the Thread in the waiting state. [Note the use of RandomCharacters.this to access the applet class’s this reference from the private inner class RunnableObject.] When the Thread should resume, the program tells all waiting threads to become ready to execute (we will discuss this shortly). However, only the resumed thread will get a chance to execute. The other suspended thread(s) will reenter the waiting state. Lines 156–177 use SwingUtilities method invokeLater to update the JLabel for the appropriate thread. This example uses an anonymous inner class to implement the Runnable interface (lines 161–175) and passes the anonymous inner class object to invokeLater. Lines 167–168 choose a random character from the alphabet string. Lines 170–172 display the character on the appropriate JLabel object.
Software Engineering Observation 15.4
An inner class can reference its outer class’s this reference by preceding the this refer- ence with the outer class name and a dot operator.
If the user clicks the Suspended check box next to a particular JLabel, the program invokes method actionPerformed (lines 93–111). The method determines which checkbox received the event. Using the index of that checkbox in array outputs, line 98 toggles the corresponding boolean in array suspended. Lines 101–102 set the background color of the JLabel to red if the thread is being suspended and green if the thread
876 |
Multithreading |
Chapter 15 |
is being resumed. If the appropriate boolean variable is false, the program calls method notifyAll (line 106) to move all waiting threads into the ready state and prepare them to resume execution. When each thread is dispatched to the processor to resume execution, the while condition at lines 139–140 in the run method fails for the resumed thread and the loop terminates. Execution of the run method then continues from line 156. For any other threads that became ready, but still are suspended, the condition at lines 139– 140 remains true and the threads reenter the waiting state.
The applet’s stop method (lines 79–90) is provided to stop all three threads if the user leaves the Web page on which this applet resides (you can simulate this by selecting Stop from the appletviewer’s Applet menu). The for loop at lines 84–85 sets each Thread reference in array threads to null. Line 89 invokes Object method notifyAll to ensure that all waiting threads get ready to execute. When the program encounters the while loop condition at line 129 for each thread, the condition fails and the run method terminates. Thus, each thread dies. If the user returns to the Web page, the applet container calls the applet’s start method to instantiate and start three new threads.
Performance Tip 15.5
Stopping applet threads when leaving a Web page is a polite programming practice because it prevents your applet from using processor time (which can reduce performance) on the browser’s machine when the applet is not being viewed. The threads can be restarted from the applet’s start method, which is invoked by the browser when the Web page is revisited by the user.
15.11 Thread Groups
Sometimes it is useful to identify various threads as belonging to a thread group; class ThreadGroup contains methods for creating and manipulating thread groups. At constructor time, the group is given a unique name via a String argument.
The threads in a thread group can be manipulated as a group. It may, for example, be desirable to interrupt all the threads in a group. A thread group can be the parent thread group to a child thread group. Method calls sent to a parent thread group are also sent to all the threads in that parent’s child thread groups.
Class ThreadGroup provides two constructors. The constructor
public ThreadGroup( String stringName )
constructs a ThreadGroup with name stringName. The constructor
public ThreadGroup( ThreadGroup parentThreadGroup, String stringName )
constructs a child ThreadGroup of parentThreadGroup called stringName. Class Thread provides three constructors that enable the programmer to instantiate a
Thread and associate it with a ThreadGroup. The constructor
public Thread( ThreadGroup threadGroup, String stringName )
constructs a Thread that belongs to threadGroup and has the name stringName. This constructor is normally invoked for derived classes of Thread whose objects should be associated with a ThreadGroup.