Chapter 14: Waiting for the Full Timeout

Chapter 16 - The SureStop Utility

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

The SureStop Class
SureStop (see Listing 16.1) has only one public method, ensureStop() . In addition, this method is static and is used universally to access a single instance of SureStop . When the SureStop class is loaded, a single instance is created using the private constructor. Inside SureStop , a thread is running to check the list of monitored threads and stop them if necessary. This internal thread is a daemon thread, so it continues to run as long as there are other threads running in the VM, and automatically dies when no other non-daemon threads remain (see Chapter 5 ). When SureStop is compiled, the compiler issues an expected warning because the deprecated stop() method is used.
Listing 16.1  SureStop.javaThe SureStop Utility
  1: import java.util.*;
  2:
  3: public class SureStop extends Object {
  4:     // nested internal class for stop request entries
  5:     private static class Entry extends Object {
  6:         private Thread thread;
  7:         private long stopTime;
  8:
  9:         private Entry(Thread t, long stop) {
10:             thread = t;
11:             stopTime = stop;
12:         }
13:     }
14:
15:     // static reference to the singleton instance
16:     private static SureStop ss;
17:
18:     static {
19:         // When class is loaded, create exactly one instance
20:         // using the private constructor.
21:         ss = new SureStop();
22:     }
23:
24:     private List stopList;
25:     private List pendingList;
26:     private Thread internalThread;
27:
28:     private SureStop() {
29:         // using a linked list for fast deletions
30:         stopList = new LinkedList();
31:
32:         // Enough initial capacity for 20 pending additions,
33:         // will grow automatically if necessary to keep
34:         // ensureStop() from blocking.
35:         pendingList = new ArrayList(20);
36:
37:         Runnable r = new Runnable() {
38:                 public void run() {
39:                     try {
40:                         runWork();
41:                     } catch (Exception x) {
42:                         // in case ANY exception slips through
43:                         x.printStackTrace();
44:                     }
45:                 }
46:             };
47:
48:         internalThread = new Thread(r);
49:         internalThread.setDaemon(true); // no need to run alone
50:         internalThread.setPriority(Thread.MAX_PRIORITY); // high
51:         internalThread.start();
52:     }
53:
54:     private void runWork() {
55:         try {
56:             while (true) {
57:                 // Since this is a super-high priority thread,
58:                 // be sure to give other threads a chance to
59:                 // run each time through in case the wait on
60:                 // pendingList is very short.
61:                 Thread.sleep(500);
62:
63:                 // Stop expired threads and determine the
64:                 // amount of time until the next thread is
65:                 // due to expire.
66:                 long sleepTime = checkStopList();
67:
68:                 synchronized (pendingList) {
69:                     if (pendingList. size () < 1) {
70:                         pendingList.wait(sleepTime);
71:                     }
72:
73:                     if (pendingList.size() > 0) {
74:                         // Copy into stopList and then remove
75:                         // from pendingList.
76:                         stopList.addAll(pendingList);
77:                         pendingList.clear();
78:                     }
79:                 }
80:             } // while
81:         } catch (InterruptedException x) {
82:             // ignore
83:         } catch (Exception x) {
84:             // Never expect this, but print a trace in case
85:             // it happens.
86:             x.printStackTrace();
87:         }
88:     }
89:
90:     private long checkStopList() {
91:         // called from runWork() by the internal thread
92:
93:         long currTime = System.currentTimeMillis();
94:         long minTime = Long.MAX_VALUE;
95:
96:         Iterator iter = stopList.iterator();
97:         while (iter.hasNext()) {
98:             Entry entry = (Entry) iter.next();
99:
100:             if (entry.thread.isAlive()) {
101:                 if (entry.stopTime < currTime) {
102:                     // timed out, stop it abruptly right now
103:                     try {
104:                         entry.thread.stop();
Listing 16.1  Continued
105:                     } catch (SecurityException x) {
106:                         // Catch this here so that other
107:                         // operations are not disrupted. Warn
108:                         // that thread could not be stopped .
109:                         System.err.println(
110:                             SureStop was not permitted to +
111:                             stop thread= + entry.thread);
112:                         x.printStackTrace();
113:                     }
114:
115:                     // Since it has stopped, remove it
116:                     // from stopList.
117:                     iter.remove();
118:                 } else {
119:                     // Not yet expired, check to see if this
120:                     // is the new minimum.
121:                     minTime = Math.min(entry.stopTime, minTime);
122:                 }
123:             } else {
124:                 // Thread died on its own, remove it from
125:                 // stopList.
126:                 iter.remove();
127:             } // if alive
128:         } // while
129:
130:         long sleepTime = minTime - System.currentTimeMillis();
131:
132:         // ensure that it is a least a little bit of time
133:         sleepTime = Math.max(50, sleepTime);
134:
135:         return sleepTime;
136:     }
137:
138:     private void addEntry(Entry entry) {
139:         // called from ensureStop() by external thread
140:
141:         synchronized (pendingList) {
142:             pendingList.add(entry);
143:
144:             // no need for notifyAll(), one waiter
145:             pendingList.notify();
146:         }
147:     }
148:
149:     public static void ensureStop(Thread t, long msGracePeriod) {
150:         if (!t.isAlive()) {
151:             // thread is already stopped, return right away
152:             return;
153:         }
154:
155:         long stopTime =
156:                 System.currentTimeMillis() + msGracePeriod;
157:
158:         Entry entry = new Entry(t, stopTime);
159:         ss.addEntry(entry);
160:     }
161: }
A private nested class called Entry (lines 513) is used by SureStop to encapsulate the data needed to monitor threads. In its constructor (lines 912), it is passed a reference to the thread to monitor and the system clock time after which this thread should be stopped.
When the SureStop class is brought into memory by the virtual machines class loader, its static block (lines 1822) is executed. In the static block, the private constructor is used to create a single instance of SureStop that monitors all stop requests . A reference to this singleton is stored in the private , static member variable ss (line 16).
The private constructor for SureStop (lines 2852) initializes its internal lists and starts its internal thread. This class uses the Collections API to manipulate lists. The list of threads currently being monitored is held in stopList (lines 24, 30). (Any implementation of List would do, but I chose to use LinkedList because of its support for efficient deletion of elements from the middle of the list.) A second list is used as a temporary store for recent additions: pendingList (lines 25, 35). (Again, any implementation of List would do, but I chose to use an ArrayList because it would typically hold only a couple of elements until they could be bulk- copied into stopList .) Two lists are used so that new entries can be added quickly with minimal blocking. The ensureStop() method invokes addEntry() where the new entry is added right away to pendingList . Because pendingList is caching new entries, the internal thread is free to iterate through stopList without interference. When the internal thread gets a chance, it moves the entries from pendingList into stopList .
The remainder of the constructor (lines 3751) basically follows the self-running object pattern with minor additions. The internal thread is marked as a daemon thread so that it will automatically stop when all the other non-daemon threads in the VM die (line 49). This way, the thread inside SureStop will not keep the VM from exiting. Additionally, the internal thread is set to run at the highest available priority (line 50). This is done so that the internal thread will get a chance to run and stop other high-priority threads. If the internal thread was not run at the highest priority, it is possible that it would not get a chance to stop a higher-priority thread. Careful steps are taken in the main loop to be sure that the internal thread blocks for significant amounts of time, giving other threads in the VM a chance to run.
The internal thread spends the bulk of its time inside the runWork() method (lines 5488). The infinite while loop (lines 5680) continues to run until the VM exits. Each time through, internalThread sleeps for half a second simply to be sure that other threads are given a chance to run (line 61). It is not critical that a monitored thread be stopped exactly on timeit can be stopped a little bit late. Next, stopList is checked to see if any monitored threads have died on their own, or if any have timed out and need to be stopped abruptly (line 66). The time interval returned from stopList indicates how long the internal thread should wait before checking stopList again.
The synchronized block inside runWork() (lines 6879) controls concurrent access to pendingList . If no items have been added to pendingList since the last time it was checked (line 69), the internal thread waits up to sleepTime milliseconds (line 70) for elements to be added by addEntry() . The internal thread gets to line 73 because of one of the following: pendingList was not empty and no waiting was done, an item was added to pendingList while waiting, or sleepTime milliseconds elapsed while waiting. If there are any elements currently in pendingList (line 73), they are copied into stopList (line 76) and deleted from pendingList (line 77). The while loop is then executed again.
  Note There is no need for synchronized access to stopList because internalThread is the one and only thread that ever interacts with the list. On the other hand, synchronized access to pendingList is necessary since that list is accessed by internalThread and all the threads that invoke ensureStop() .
The checkStopList() method (lines 90136) scans stopList , removing threads that have stopped on their own and stopping threads that have timed out. It also returns the amount of time remaining until the next monitored thread times out. An Iterator is used to traverse the LinkedList and extract the entries (lines 9698). If stopList is empty, the while loop (lines 97128) is bypassed and a very large sleepTime is returned ( Long.MAX_VALUE ). If stopList has Entry objects in it, each is checked to see if the thread being monitored is still alive (line 100). If it is not alive, it is simply removed from stopList (lines 123127).
If the thread is still alive, its stopTime is checked against the current time to see if it should be stopped (line 101). If the monitored thread has timed out, it is stopped (line 104). If SureStop is not permitted to stop this thread, a SecurityException is thrown (and caught) and a message is printed (lines 105113). Whether or not stop() is successful, the entry is removed from stopList (line 117). If it was not time to stop the monitored thread and it is still alive, the entrys stopTime is compared with minTime to determine if it is sooner than the value already held in minTime (line 121).
When all the entries have been checked, a new sleepTime is calculated. The entry that is due to expire sooner than any of the others has its stopTime stored in minTime . The difference between minTime and the current time is used for sleepTime (line 130). If sleepTime is less than 50 milliseconds, it is bumped up to 50 milliseconds (line 133) so that there is some delay.
The addEntry() method (lines 138147) is used by ensureStop() to add new Entry objects o pendingList . The calling thread blocks until an exclusive lock can be acquired on pendingList (line 141). If this blocks at all, it will be for a very short time while internalThread moves the entries from pendingList to stopList . The Entry reference is added (line 142), and notify() is invoked to signal internalThread if it is waiting (line 145). In this particular case, notifyAll() is not necessary as there is at most one thread waiting.
The only external interface into SureStop is through its only public method: ensureStop() (lines 149160). It is passed a reference to the thread to be monitored and the number of illiseconds to wait before abruptly stopping it (line 149). If the thread is already dead, ensureStop() returns right away (lines 150153). If not, a new Entry object is created with a stopTime that is the current time plus the timeout value (lines 155158). This new Entry instance is added to pendingList by invoking addEntry() on the SureStop singleton (line 159).

Toc


Java Thread Programming
Java Thread Programming
ISBN: 0672315858
EAN: 2147483647
Year: 2005
Pages: 149
Authors: Paul Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net