Monday, November 2, 2009

The Best Replacement for stop(), suspend(), and resume()




















Chapter 5 - Gracefully Stopping Threads

Java Thread Programming
Paul
Hyde






size=1>  size=2>Copyright � 1999 Sams
Publishing




















size=4>The Best Replacement for stop(), suspend(), and
resume()












size=2>The class size=2>BestReplacement
presented in Listing 5.11 combines some techniques from other
chapters to create a model class that effectively eliminates the
need for the three deprecated methods
face="Courier New" size=2>stop() face=Arial size=2>, size=2>suspend(), and
size=2>resume() size=2>.












size=2>Listing 5.11  BestReplacement.java—Combined
Alternative Techniques












face="Courier New" size=2>  1: // uses BooleanLock from chapter
17










face="Courier New" size=2>  2:










face="Courier New" size=2>  3: public class BestReplacement
extends Object {










face="Courier New" size=2>  4:     private
Thread internalThread;










face="Courier New" size=2>  5:     private
volatile boolean stopRequested;










face="Courier New" size=2>  6:










face="Courier New" size=2>  7:     private
BooleanLock suspendRequested;










face="Courier New" size=2>  8:     private
BooleanLock internalThreadSuspended;










face="Courier New" size=2>  9:










face="Courier New" size=2>10:     public
BestReplacement() {










face="Courier New"
size=2>11:        
stopRequested = false;










face="Courier New" size=2>12:










face="Courier New"
size=2>13:        
suspendRequested = new BooleanLock(false);










face="Courier New"
size=2>14:        
internalThreadSuspended = new BooleanLock(false);










face="Courier New" size=2>15:










face="Courier New"
size=2>16:         Runnable
r = new Runnable() {










face="Courier New"
size=2>17:                
public void run() {










face="Courier New"
size=2>18:                    
try {










face="Courier New"
size=2>19:                        
runWork();










face="Courier New"
size=2>20:                    
} catch ( Exception x ) {










face="Courier New"
size=2>21:                        
// in case ANY exception slips through










face="Courier New"
size=2>22:                        
x.printStackTrace();










face="Courier New"
size=2>23:                    
}










face="Courier New"
size=2>24:                
}










face="Courier New"
size=2>25:            
};










face="Courier New" size=2>26:










face="Courier New"
size=2>27:        
internalThread = new Thread(r);










face="Courier New"
size=2>28:        
internalThread.start();










face="Courier New" size=2>29:     }










face="Courier New" size=2>30:










face="Courier New" size=2>31:     private void
runWork() {










face="Courier New"
size=2>32:         int count
= 0;










face="Courier New" size=2>33:










face="Courier New"
size=2>34:         while (
!stopRequested ) {










face="Courier New"
size=2>35:            
try {










face="Courier New"
size=2>36:                
waitWhileSuspended();










face="Courier New"
size=2>37:            
} catch ( InterruptedException x ) {










face="Courier New"
size=2>38:                
// Reassert interrupt so that remaining code










face="Courier New"
size=2>39:                
// sees that an interrupt has been requested.










face="Courier New"
size=2>40:                
Thread.currentThread().interrupt();










face="Courier New" size=2>41:










face="Courier New"
size=2>42:                
// Reevaluate while condition --probably










face="Courier New"
size=2>43:                
// false now.










face="Courier New"
size=2>44:                
continue;










face="Courier New"
size=2>45:            
}










face="Courier New" size=2>46:










face="Courier New"
size=2>47:            
System.out.println(“Part I - count=” + count);










face="Courier New" size=2>48:










face="Courier New"
size=2>49:            
try {










face="Courier New"
size=2>50:                
Thread.sleep(1000);










face="Courier New"
size=2>51:            
} catch ( InterruptedException x ) {










face="Courier New"
size=2>52:                
Thread.currentThread().interrupt(); // reassert










face="Courier New"
size=2>53:                
// continue on as if sleep completed normally










face="Courier New"
size=2>54:            
}










face="Courier New" size=2>55:










face="Courier New"
size=2>56:            
System.out.println(“Part II - count=” + count);










face="Courier New" size=2>57:










face="Courier New"
size=2>58:            
try {










face="Courier New"
size=2>59:                
Thread.sleep(1000);










face="Courier New"
size=2>60:            
} catch ( InterruptedException x ) {










face="Courier New"
size=2>61:                
Thread.currentThread().interrupt(); // reassert










face="Courier New"
size=2>62:                
// continue on as if sleep completed normally










face="Courier New"
size=2>63:            
}










face="Courier New" size=2>64:










face="Courier New"
size=2>65:            
System.out.println(“Part III - count=” + count);










face="Courier New" size=2>66:










face="Courier New"
size=2>67:            
count++;










face="Courier New"
size=2>68:        
}










face="Courier New" size=2>69:     }










face="Courier New" size=2>70:










face="Courier New" size=2>71:     private void
waitWhileSuspended()










face="Courier New"
size=2>72:                    
throws InterruptedException {










face="Courier New" size=2>73:










face="Courier New"
size=2>74:         // only
called by the internal thread - private method










face="Courier New" size=2>75:










face="Courier New"
size=2>76:        
synchronized ( suspendRequested ) {










face="Courier New"
size=2>77:            
if ( suspendRequested.isTrue() ) {










face="Courier New"
size=2>78:                
try {










face="Courier New"
size=2>79:                    
internalThreadSuspended.setValue(true);










face="Courier New"
size=2>80:                    
suspendRequested.waitUntilFalse(0);










face="Courier New"
size=2>81:                
} finally {










face="Courier New"
size=2>82:                    
internalThreadSuspended.setValue(false);










face="Courier New"
size=2>83:                
}










face="Courier New"
size=2>84:            
}










face="Courier New"
size=2>85:        
}










face="Courier New" size=2>86:     }










face="Courier New" size=2>87:










face="Courier New" size=2>88:     public void
suspendRequest() {










face="Courier New"
size=2>89:        
suspendRequested.setValue(true);










face="Courier New" size=2>90:     }










face="Courier New" size=2>91:










face="Courier New" size=2>92:     public void
resumeRequest() {










face="Courier New"
size=2>93:        
suspendRequested.setValue(false);










face="Courier New" size=2>94:     }










face="Courier New" size=2>95:










face="Courier New" size=2>96:     public boolean
waitForActualSuspension(long msTimeout)










face="Courier New"
size=2>97:                    
throws InterruptedException {










face="Courier New" size=2>98:










face="Courier New"
size=2>99:         //
Returns ‘true’ if suspended, ‘false’ if the










face="Courier New"
size=2>100:         //
timeout expired.










face="Courier New" size=2>104:










face="Courier New" size=2>105:     public void
stopRequest() {










face="Courier New"
size=2>106:        
stopRequested = true;










face="Courier New"
size=2>107:        
internalThread.interrupt();










face="Courier New" size=2>108:     }










face="Courier New" size=2>109:










face="Courier New" size=2>110:     public
boolean isAlive() {










face="Courier New"
size=2>111:         return
internalThread.isAlive();










face="Courier New" size=2>112:     }










face="Courier New" size=2>113:










face="Courier New" size=2>114:     public static
void main(String[] args) {










face="Courier New"
size=2>115:         try
{










face="Courier New"
size=2>116:            
BestReplacement br = new BestReplacement();










face="Courier New"
size=2>117:            
System.out.println(










face="Courier New"
size=2>118:                    
“--> just created, br.isAlive()=” +










face="Courier New"
size=2>119:                    
br.isAlive());










face="Courier New"
size=2>120:            
Thread.sleep(4200);










face="Courier New" size=2>121:










face="Courier New"
size=2>122:            
long startTime = System.currentTimeMillis();










face="Courier New"
size=2>123:            
br.suspendRequest();










face="Courier New"
size=2>124:            
System.out.println(










face="Courier New"
size=2>125:                    
“--> just submitted a suspendRequest”);










face="Courier New" size=2>126:










face="Courier New"
size=2>127:            
boolean suspensionTookEffect =










face="Courier New"
size=2>128:                    
br.waitForActualSuspension(10000);










face="Courier New"
size=2>129:            
long stopTime = System.currentTimeMillis();










face="Courier New" size=2>130:










face="Courier New"
size=2>131:            
if ( suspensionTookEffect ) {










face="Courier New"
size=2>132:                
System.out.println(










face="Courier New"
size=2>133:                    
“--> the internal thread took “ +










face="Courier New"
size=2>134:                    
(stopTime - startTime) + “ ms to notice “ +










face="Courier New"
size=2>135:                    
“\n    the suspend request and is now “ +










face="Courier New"
size=2>136:                    
“suspended.”);










face="Courier New"
size=2>137:            
} else {










face="Courier New"
size=2>138:                
System.out.println(










face="Courier New"
size=2>139:                    
“--> the internal thread did not notice “ +










face="Courier New"
size=2>140:                    
“the suspend request “ +










face="Courier New"
size=2>141:                    
“\n    within 10 seconds.”);










face="Courier New"
size=2>142:            
}










face="Courier New" size=2>143:










face="Courier New"
size=2>144:            
Thread.sleep(5000);










face="Courier New" size=2>145:










face="Courier New"
size=2>146:            
br.resumeRequest();










face="Courier New"
size=2>147:            
System.out.println(










face="Courier New"
size=2>148:                    
“--> just submitted a resumeRequest”);










face="Courier New"
size=2>149:            
Thread.sleep(2200);










face="Courier New" size=2>150:










face="Courier New"
size=2>151:            
br.stopRequest();










face="Courier New"
size=2>152:            
System.out.println(










face="Courier New"
size=2>153:                    
“--> just submitted a stopRequest”);










face="Courier New"
size=2>154:         } catch
( InterruptedException x ) {










face="Courier New"
size=2>155:            
// ignore










face="Courier New"
size=2>156:        
}










face="Courier New" size=2>157:     }










face="Courier New" size=2>158: }












size=2>In writing size=2>BestReplacement,
I used some techniques from other chapters. It is a
color=#010100 face=Arial size=2>self-running
object
(for more
details, see the technique in
color=#008000 face=Arial size=2> Chapter 11, “Self-Running
Objects”) in the
sense that from outside the class, there is no indication that a
thread will be running internally (although the class’s
documentation should mention this fact). A user of this class does
not have to create a thread to run within it; one is created (line
27) and started (line 28) automatically in the constructor. The
reference to this thread is held in a
face="Courier New" size=2>private face=Arial size=2> member variable (line 4). The color=#000000 face="Courier New" size=2>Runnable color=#000000 face=Arial size=2> used by the internal thread is an
inner class (lines 16–25). By using an inner class, the
color=#000000 face="Courier New" size=2>public void
run()
method is hidden
and cannot be erroneously called by external code. Within this inner
class, the
size=2>private void runWork() size=2> method is invoked by the internal thread (line 19). In your
design, if the thread should not be started in the constructor, you
can include another method to allow the
face="Courier New" size=2>internalThread.start() color=#000000 face=Arial size=2> operation to be performed when
appropriate.












size=2>Additionally, the face="Courier New" size=2>BooleanLock face=Arial size=2> class is used from color=#008000 face=Arial size=2>Chapter 17 color=#000000 face=Arial size=2>. face="Courier New" size=2>BooleanLock face=Arial size=2> encapsulates the wait/notify mechanism inside a
class that holds a
size=2>boolean value.
The
size=2>setValue()
method is used to change the internal value and signal any and all
threads waiting for the value to change. Other threads can wait for
the value to be changed by invoking methods like
color=#000000 face="Courier New" size=2>waitUntilTrue() color=#000000 face=Arial size=2> and face="Courier New" size=2>waitUntilFalse() face=Arial size=2>. In size=2>BestReplacement,
two instances of
size=2>BooleanLock are
used. The
size=2>suspendRequested
instance (line 7) is used to track whether a suspend has been
requested. The
size=2>internalThreadSuspended size=2> instance (line 8) is used to determine if the internal
thread has noticed a suspend request. Both are initially set to
size=2>false (lines
13–14).












size=2>Inside the size=2>runWork()
method, the
size=2>while loop
(lines 34–68) continues until
face="Courier New" size=2>stopRequested face=Arial size=2> is size=2>true. Each time
through,
size=2>waitWhileSuspended() size=2> is called (line 36) to block, if currently suspended. This
is a
safe
place
for the
internal thread to be suspended or stopped. If the internal thread
is interrupted while waiting, it will throw an
color=#010100 face="Courier New"
size=2>InterruptedException
size=2>. The interrupt is used only to signal that the thread should
die as soon as possible. This exception is caught, and the thread is
reinterrupted (line 40) in case any other remaining statement
becomes stuck and jumps back up to the top of the
color=#010100 face="Courier New" size=2>while color=#010100 face=Arial size=2> because of the color=#010100 face="Courier New" size=2>continue color=#010100 face=Arial size=2> statement (line 44).












size=2>If not currently suspended, or after being resumed, the
internal thread proceeds through the statements and prints various
messages (lines 47, 56, and 65). Several sleeps are used and if
interrupted, catch the exception and reassert the interrupted status
(lines 52 and 61).












size=2>In size=2>suspendRequest() size=2>, the size=2>suspendRequested
instance of
size=2>BooleanLock has
its value set to
size=2>true (line 89).
In
size=2>resumeRequest(),
size=2>suspendRequest
is set to
size=2>false (line 93).
All of the synchronization and notification necessary for changing
the value is encapsulated inside
face="Courier New" size=2>BooleanLock face=Arial size=2>.












size=2>In size=2>waitWhileSuspended() size=2> (lines 71–86), a busy wait is avoided by using a
size=2>BooleanLock
instance (
size=2>BooleanLock uses
the wait/notify mechanism internally). First, the internal thread
blocks until it can acquire exclusive access to the object-level
lock on
size=2>suspendRequested
(line 76). If it is currently suspended, it enters the
color=#010100 face="Courier New" size=2>try color=#010100 face=Arial size=2>/ face="Courier New" size=2>finally face=Arial size=2> block (lines 77–84); otherwise, it simply
returns. In the
size=2>try/ color=#010100 face="Courier New" size=2>catch color=#010100 face=Arial size=2> block, the internal thread
indicates that it has noticed the suspend request by setting the
state of
size=2>internalThreadSuspened size=2> to size=2>true (line 79).
The internal thread then invokes the
face="Courier New" size=2>waitUntilFalse() face=Arial size=2> method of face="Courier New" size=2>suspendRequested face=Arial size=2> with a timeout of face="Courier New" size=2>0 size=2> to indicate that it should never timeout (line 80). No
matter what happens, when the internal thread leaves the
try color=#010100 face=Arial size=2> section, it enters the color=#010100 face="Courier New" size=2>finally color=#010100 face=Arial size=2> section where the state of
size=2>internalThreadSuspended size=2> is set back to size=2>false (line
82).












size=2>If an external thread wants to know if the internal thread
has noticed the suspend request, the external thread can invoke
size=2>waitForActualSuspension() face=Arial size=2> (lines 96–103). This blocks waiting (up to the
timeout) until
size=2>internalThreadSuspended size=2> is set to size=2>true.














  size=2>Tip size=2> size=2> color=#000000 face="Courier New" size=2> face=Arial size=2> color=#008000 face=Arial size=2> face=Arial size=2> color=#008000 face=Arial size=2> face=Arial size=2> color=#008000 face=Arial size=2> face=Arial size=2> size=2>Don’t
worry if you don’t fully understand the use of
color=#000000 face="Courier New" size=2>synchronized color=#000000 face=Arial size=2> and the wait/notify mechanism
encapsulated in
size=2>BooleanLock at
this time. I fully explain them in
color=#008000 face=Arial size=2>Chapters 7 color=#000000 face=Arial size=2>, color=#008000 face=Arial size=2>8 face=Arial size=2>, and color=#008000 face=Arial size=2>17 face=Arial size=2>.












size=2>The internal thread can be stopped by invoking color=#010100 face="Courier New" size=2>stopRequest() color=#010100 face=Arial size=2>. This method first sets the
size=2>stopRequest
size=2>boolean flag to
size=2>true (line 106).
It then interrupts the internal thread to “unstick” it from any
blocking sleeps or waits (line 107). The
face="Courier New" size=2>isAlive() face=Arial size=2> method is used to check whether the internal
thread has died (line 111).












size=2>When run, output such as the following is produced:












face="Courier New" size=2>--> just created,
br.isAlive()=true










face="Courier New" size=2>Part I - count=0










face="Courier New" size=2>Part II - count=0










face="Courier New" size=2>Part III - count=0










face="Courier New" size=2>Part I - count=1










face="Courier New" size=2>Part II - count=1










face="Courier New" size=2>Part III - count=1










face="Courier New" size=2>Part I - count=2










face="Courier New" size=2>--> just submitted a
suspendRequest










face="Courier New" size=2>Part II - count=2










face="Courier New" size=2>Part III - count=2










face="Courier New" size=2>--> the internal thread took 1810 ms to
notice










face="Courier New" size=2>    the suspend request and
is now suspended.










face="Courier New" size=2>--> just submitted a
resumeRequest










face="Courier New" size=2>Part I - count=3










face="Courier New" size=2>Part II - count=3










face="Courier New" size=2>Part III - count=3










face="Courier New" size=2>Part I - count=4










face="Courier New" size=2>--> just submitted a
stopRequest










face="Courier New" size=2>Part II - count=4










face="Courier New" size=2>Part III - count=4












size=2>Notice that when a suspend request is submitted, the loop
continues until the suspend check at the top (
color=#010100 face="Courier New"
size=2>waitWhileSuspended()
size=2>). Also notice that when stopped, the internal thread does
not immediately terminate, but instead finishes its tasks in an
orderly manner.














  size=2>Caution size=2> size=2> color=#000000 face="Courier New" size=2> face=Arial size=2> size=2> color=#000000 face="I Frutiger Italic" size=2> color=#000000 face=Arial size=2> face="Courier New" size=2> size=2> color=#008000 face=Arial size=2> size=2>Keep in
mind that this stopping feature requires that a blocked statement
respond to an interrupt. This is not always the case. For example,
the
size=2>read() method on
an
size=2>InputStream
blocks until a new byte is available, the end of stream is reached,
or an
size=2>IOException is
thrown. It does
size=2>not throw an
size=2>InterruptedException size=2> if the blocked thread is interrupted! A technique for
dealing with this situation is offered in
color=#008000 face=Arial size=2>Chapter 15, “Breaking Out of a
Blocked I/O State.”












valign="top" bgcolor="#FFFFFF">

Toc



No comments:

Post a Comment