Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Java How to Program, Fourth Edition - Deitel H., Deitel P.pdf
Скачиваний:
58
Добавлен:
24.05.2014
Размер:
14.17 Mб
Скачать

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.