Chapter 5. Gracefully Stopping Threads

IN THIS CHAPTER

In all the examples presented so far in this book, new threads stop running when they die a natural death by returning from the run() method. At times, a thread may be busy doing some involved task for a while (maybe indefinitely) or perhaps may be sleeping for a long time. Sometimes it is necessary to pause or terminate a thread that is executing a lengthy operation whose duration could not be predetermined because of external dependencies like waiting on I/O or for a signal (notification) from another thread. This chapter explores mechanisms used to suspend, resume, and permanently stop a thread's execution. This is an area where several methods presented in JDK 1.0 and 1.1 have been deprecated in JDK 1.2. I'll offer alternative techniques to virtually eliminate the necessity to use the deprecated methods.

Interrupting a Thread: interrupt()

While one thread is running, another thread can interrupt it by invoking its corresponding Thread object's interrupt() method:

public void interrupt()

This method simply sets a flag in the destination thread indicating that it has been interrupted and returns right away. It is possible that a SecurityException will be thrown by interrupt(), indicating that the thread requesting the interrupt does not have permission to interrupt the other thread. The security check is done by invoking the checkAccess() method on Thread, which in turn checks whether a SecurityManager has been installed, and if so, invokes its checkAccess(Thread) method. An in-depth exploration of security in Java is beyond the scope of this book, but you should note that some methods of Thread and ThreadGroup could throw SecurityExceptions.

Caution

SecurityException is a subclass of RuntimeException, so try/catch blocks are not required for any of the methods of Thread or ThreadGroup that might throw it. By default, an application does not have a SecurityManager defined (using JDK 1.0, 1.1, or 1.2 from Sun Microsystems). An applet, on the other hand, might have a SecurityManager present.

In general, provisions are not made throughout the code to catch this type of exception, but a general precheck may be done early in the application or applet code. To check whether a SecurityManager is present, use the static method System.getSecurityManager(). If it returns null, a SecurityManager is not installed. If it is not null, caution must be used when invoking methods that might throw a SecurityException.

Interrupting a Sleeping Thread

Listing 5.1 shows how you can use interrupt()to disturb a sleeping thread and cause it to throw an InterruptedException.

Example 5.1. SleepInterrupt.java—Interrupting a Sleeping Thread

 1: public class SleepInterrupt extends Object implements Runnable {
 2:     public void run() {
 3:         try {
 4:             System.out.println(
 5:                     "in run() - about to sleep for 20 seconds");
 6:             Thread.sleep(20000);
 7:             System.out.println("in run() - woke up");
 8:         } catch ( InterruptedException x ) {
 9:             System.out.println(
10:                     "in run() - interrupted while sleeping");
11:             return;
12:         }
13:
14:         System.out.println("in run() - doing stuff after nap");
15:         System.out.println("in run() - leaving normally");
16:     }
17:
18:
19:     public static void main(String[] args) {
20:         SleepInterrupt si = new SleepInterrupt();
21:         Thread t = new Thread(si);
22:         t.start();
23:
24:         // Be sure that the new thread gets a chance to
25:         // run for a while.
26:         try { Thread.sleep(2000); } 
27:         catch ( InterruptedException x ) { }
28:
29:         System.out.println(
30:                 "in main() - interrupting other thread");
31:         t.interrupt();
32:         System.out.println("in main() - leaving");
33:     }
34: }

When SleepInterrupt is run, the following output occurs:

in run() - about to sleep for 20 seconds
in main() - interrupting other thread
in main() - leaving
in run() - interrupted while sleeping

Note that because of indeterminate thread scheduling, the last two lines might be swapped, but the first two will definitely appear in their current order.

After starting a second thread (line 22), the main thread proceeds to sleep for two seconds (line 26) to allow the new thread time to get up and running. The new thread prints its about to sleep message (lines 4–5) and proceeds to sleep for 20 seconds (line 6). About two seconds later, the main thread signals an interrupt to the new thread (line 31), prints its leaving message (line 32), and dies. Because it is interrupted, the new thread's 20-second nap is abruptly stopped, with about 18 seconds remaining, and sleep() throws an InterruptedException (line 6). Execution jumps ahead to the catch block (line 8). The new thread prints its interrupted message (lines 9–10), returns right away from run() (line 11), and dies.

Pending Interrupts

The SleepInterrupt example shows that the implementation of the sleep() method is kind enough to stop sleeping and throw an InterruptedException if it detects that the sleeping thread is interrupted. Additionally, if an interrupt is pending before the sleep() method is called, it immediately throws an InterruptedException. This is demonstrated in PendingInterrupt, shown in Listing 5.2.

Example 5.2. PendingInterrupt.java—An Interrupt Can Occur Before It Is Checked For

 1: public class PendingInterrupt extends Object {
 2:     public static void main(String[] args) {
 3:         if ( args.length > 0 ) {
 4:             Thread.currentThread().interrupt();
 5:         }
 6:
 7:         long startTime = System.currentTimeMillis();
 8:         try {
 9:             Thread.sleep(2000);
10:             System.out.println("was NOT interrupted");
11:         } catch ( InterruptedException x ) {
12:             System.out.println("was interrupted");
13:         }
14:
15:         System.out.println(
16:                 "elapsedTime=" + ( System.currentTimeMillis() - startTime ));
17:     }
18: }

If PendingInterrupt is run without any command-line arguments, as follows,

java PendingInterrupt

it produces output much like this:

was NOT interrupted
elapsedTime=2080

In this mode, it simply sleeps for the specified two seconds and prints out the message that it was not interrupted. Alternatively, if PendingInterrupt is run with any command-line arguments, as follows,

java PendingInterrupt yes

it would instead produce output much like this:

was interrupted
elapsedTime=110

In this mode, the main thread interrupts itself (line 4). This does not have any effect other than setting an interrupted flag (which is internal to Thread) to true. Although interrupted, the main thread keeps running. The main thread proceeds to make note of the current real-time clock (line 7) and enters the try block (line 8). As soon as the sleep() is invoked (line 9), it notices that there is a pending interrupt and immediately throws an InterruptedException. Execution jumps to the catch block (line 11) and prints the message that the thread was interrupted. Finally, the elapsed time is calculated and printed.

Using isInterrupted()

You can check the interrupted status of any thread by invoking the isInterrupted() method on the Thread object:

public boolean isInterrupted()

This does not alter the status, but simply returns true if the thread has been interrupted and its interrupted flag has not yet been cleared. Listing 5.3 shows an example of how you can use isInterrupted().

Example 5.3. InterruptCheck.java—Using isInterrupted()

 1: public class InterruptCheck extends Object {
 2:     public static void main(String[] args) {
 3:         Thread t = Thread.currentThread();
 4:         System.out.println("Point A: t.isInterrupted()=" + t.isInterrupted());
 5:         t.interrupt();
 6:         System.out.println("Point B: t.isInterrupted()=" + t.isInterrupted());
 7:         System.out.println("Point C: t.isInterrupted()=" + t.isInterrupted());
 8:
 9:         try {
10:             Thread.sleep(2000);
11:             System.out.println("was NOT interrupted");
12:         } catch ( InterruptedException x ) {
13:             System.out.println("was interrupted");
14:         }
15:
16:         System.out.println("Point D: t.isInterrupted()=" + t.isInterrupted());
17:     }
18: }

Running InterruptCheck produces the following output:

Point A: t.isInterrupted()=false
Point B: t.isInterrupted()=true
Point C: t.isInterrupted()=true
was interrupted
Point D: t.isInterrupted()=false

Initially, at Point A, the interrupted status is false (line 4), and it is set to true by invoking interrupt() (line 5). As a result, at Point B, isInterrupted() now returns true (line 6). At Point C, isInterrupted() still returns true and is unaffected by a previous call. Because the interrupted flag is true, sleep() immediately throws an InterruptedException (line 10). Execution jumps to the catch and prints a message indicating that the thread was interrupted (line 13). Finally, at Point D, isInterrupted() returns false because the interrupted flag was cleared by sleep() when it threw the exception.

Using Thread.interrupted()

You can use the Thread.interrupted() method to check (and implicitly reset to false) the interrupted status of the current thread:

public static boolean interrupted()

Because it is static, it cannot be invoked on a particular thread, but can only report the interrupted status of the thread that invoked it. This method returns true if the thread has been interrupted and its interrupted flag has not yet been cleared. Unlike isInterrupted(), it automatically resets the interrupted flag to false. Invoking Thread.interrupted() a second time would always return false unless the thread was reinterrupted. Listing 5.4 shows an example of how you can use Thread.interrupted().

Example 5.4. InterruptReset.java—Using Thread.interrupted()

 1: public class InterruptReset extends Object {
 2:     public static void main(String[] args) {
 3:         System.out.println(
 4:             "Point X: Thread.interrupted()=" + Thread.interrupted());
 5:         Thread.currentThread().interrupt();
 6:         System.out.println(
 7:             "Point Y: Thread.interrupted()=" + Thread.interrupted());
 8:         System.out.println(
 9:             "Point Z: Thread.interrupted()=" + Thread.interrupted());
10:     }
11: }

Running InterruptReset produces the following output:

Point X: Thread.interrupted()=false
Point Y: Thread.interrupted()=true
Point Z: Thread.interrupted()=false

Initially, at Point X, the interrupted status is false (lines 3–4). The current thread interrupts itself on line 5. At Point Y, the interrupted status is true and is automatically reset back to false by Thread.interrupted() (lines 6–7). The final check of the status at Point Z shows that it is indeed false again (lines 8–9).

Using InterruptedException

A method such as Thread.sleep() is kind enough to stop sleeping and throw an InterruptedException if it is interrupted. Another method commonly used in multithreaded programming is the wait() method of Object. It is involved with inter-thread communication and is covered in detail in Chapter 8, "Inter-thread Communication." Like Thread.sleep(), it, too, throws an InterruptedException if interrupted. Many other methods in the JDK will throw an InterruptedException if the thread that entered the method is interrupted before completing its work.

The pattern of throwing an InterruptedException when interrupted by another thread is a useful one to extend to other customized methods. For example, imagine that you need a method that performs a complex and long-running calculation. It would be nice if the calculation occasionally checked whether it had been interrupted and if it had, threw an InterruptedException to stop the calculation and indicate that it did not complete normally.

Listing 5.5 shows an example of how the interrupted status can be checked and used to throw an InterruptedException if necessary. When run, the class PiInterrupt proceeds to iterate continuously to calculate the special number pi until the desired level of accuracy is achieved—or until it is interrupted.

Example 5.5. PiInterrupt.java—Throwing InterruptedException When Necessary

 1: public class PiInterrupt extends Object implements Runnable {
 2:     private double latestPiEstimate;
 3:
 4:     public void run() {
 5:         try {
 6:             System.out.println("for comparison, Math.PI=" +
 7:                                 Math.PI);
 8:             calcPi(0.000000001);
 9:             System.out.println("within accuracy, latest pi=" +
10:                                 latestPiEstimate);
11:         } catch ( InterruptedException x ) {
12:             System.out.println("INTERRUPTED!! latest pi=" +
13:                                 latestPiEstimate);
14:         }
15:     }
16:
17:     private void calcPi(double accuracy)
18:                 throws InterruptedException {
19:
20:         latestPiEstimate = 0.0;
21:         long iteration = 0;
22:         int sign = -1;
23:
24:         while ( Math.abs(latestPiEstimate - Math.PI) >
25:                 accuracy ) {
26:
27:             if ( Thread.interrupted() ) {
28:                 throw new InterruptedException();
29:             }
30:
31:             iteration++;
32:             sign = -sign;
33:             latestPiEstimate +=
34:                     sign * 4.0 / ( ( 2 * iteration ) - 1 );
35:         }
36:     }
37: 
38:     public static void main(String[] args) {
39:         PiInterrupt pi = new PiInterrupt();
40:         Thread t = new Thread(pi);
41:         t.start();
42:
43:         try {
44:             Thread.sleep(10000);
45:             t.interrupt();
46:         } catch ( InterruptedException x ) {
47:             // ignore
48:         }
49:     }
50: }

In the main thread, a new PiInterrupt instance is constructed (line 39). Because PiInterrupt implements Runnable, an instance of it can be passed to the constructor for Thread (line 40). The new Thread object is started (line 41). The main thread then proceeds to sleep for 10 seconds (line 44) before interrupting the thread running inside PiInterrupt.

While the main thread is sleeping, the newly spawned thread enters the run() method (line 4). For comparison purposes, it prints out the value of pi held as a constant in the Math class (lines 6–7). It then proceeds to call calcPi(), specifying that it should continue to refine its calculation until it is within 0.000000001 of the constant Math.PI (line 8).

The private method calcPi() declares that it might throw an InterruptedException (line 18). It will do this if interrupted before getting within the specified accuracy. The technique used to calculate pi is to sum the terms of the infinite series

pi = 4/1 - 4/3 + 4/5 - 4/7 + 4/9 - 4/11 + 4/13 - 4/15 ...

for as long as necessary to get within the specified accuracy. The while loop continues as long as the absolute value of the difference between the calculated pi and the constant stored in Math.PI differ more than the desired accuracy (lines 24–25). Each time through the loop, the interrupted flag is checked and reset to false (line 27). If the flag was true before being reset, an InterruptedException is thrown (line 28) and the method stops calculating. If the flag was false before being reset, the estimate is further refined (lines 31–34) and the while loop continues.

If the desired accuracy is reached before the interrupt, flow returns to run(), and the estimated pi value is printed (lines 9–10). If the thread is interrupted before reaching the desired accuracy, the exception is thrown and flow jumps to the catch block (line 11) where the estimate pi value is printed (lines 12–13).

When PiInterrupt is run, output such as the following should be produced:

for comparison, Math.PI=3.141592653589793
INTERRUPTED!! latest pi=3.141592091246143

In this case, the calculation took too long and was interrupted after 10 seconds. If your machine is fast enough to get within the accuracy before 10 seconds has elapsed, reduce the sleep time (by perhaps 2 seconds), recompile, and run it again.

Tip

In long-running methods, consider using this technique of checking the interrupted status and throwing an InterruptedException when necessary. It is a nice way of allowing a long-running method to be "cancelled" by another thread.

Suspending and Resuming Thread Execution

Sometimes you might want to temporarily pause or suspend an executing thread and at a later time let it resume execution. As an example, consider a program that uses a thread to animate some images by flipping through them sequentially. When the animated window is not visible (minimized or covered by another window), there isn't any necessity to continue doing the work of animation until the window is visible again. In fact, continuing to animate the images when no one can see them is wasting valuable processor cycles. I'm sure that you will come across many other situations in which a thread should temporarily stop its work.

Using the Deprecated Methods suspend() and resume()

The Thread API contains two deprecated methods that are used to temporarily stop and later restart a thread:

public final void suspend()
public final void resume()

Although these methods are deprecated and shouldn't be used in new code, I'll show you an example of their use in case you inherit some older code that you have to maintain. The code for the class VisualSuspendResume is shown in Listing 5.6.

Tip

Methods and classes are deprecated by Sun Microsystems to indicate that developers should avoid using them. A deprecated method can still be used, but when the code is compiled, a warning is issued. Deprecation is used to indicate a method or class is obsolete or dangerous and may be removed from future releases of the JDK. Although you may still use deprecated methods, you should use them sparingly and only when absolutely necessary.

Example 5.6. VisualSuspendResume.java—Suspending and Resuming Animation Using the Deprecated Methods

 1: import java.awt.*;
 2: import java.awt.event.*;
 3: import javax.swing.*;
 4:
 5: public class VisualSuspendResume
 6:             extends JPanel
 7:             implements Runnable {
 8:
 9:     private static final String[] symbolList =
10:             { "|", "/", "-", "\ ", "|", "/", "-", "\ " };
11:
12:     private Thread runThread;
13:     private JTextField symbolTF;
14:
15:     public VisualSuspendResume() {
16:         symbolTF = new JTextField();
17:         symbolTF.setEditable(false);
18:         symbolTF.setFont(new Font("Monospaced", Font.BOLD, 26));
19:         symbolTF.setHorizontalAlignment(JTextField.CENTER);
20:
21:         final JButton suspendB = new JButton("Suspend");
22:         final JButton resumeB = new JButton("Resume");
23:
24:         suspendB.addActionListener(new ActionListener() {
25:                 public void actionPerformed(ActionEvent e) {
26:                     suspendNow();
27:                 }
28:             });
29:
30:         resumeB.addActionListener(new ActionListener() {
31:                 public void actionPerformed(ActionEvent e) {
32:                     resumeNow();
33:                 }
34:             });
35: 
36:         JPanel innerStackP = new JPanel();
37:         innerStackP.setLayout(new GridLayout(0, 1, 3, 3));
38:         innerStackP.add(symbolTF);
39:         innerStackP.add(suspendB);
40:         innerStackP.add(resumeB);
41:
42:         this.setLayout(new FlowLayout(FlowLayout.CENTER));
43:         this.add(innerStackP);
44:     }
45:
46:     private void suspendNow() {
47:         if ( runThread != null ) { // avoid NullPointerException
48:             runThread.suspend();
49:         }
50:     }
51:
52:     private void resumeNow() {
53:         if ( runThread != null ) { // avoid NullPointerException
54:             runThread.resume();
55:         }
56:     }
57:
58:     public void run() {
59:         try {
60:             // Store this for the suspendNow() and
61:             // resumeNow() methods to use.
62:             runThread = Thread.currentThread();
63:             int count = 0;
64:
65:             while ( true ) {
66:                 // each time through, show the next symbol
67:                 symbolTF.setText(
68:                     symbolList[ count % symbolList.length ]);
69:                 Thread.sleep(200);
70:                 count++;
71:             }
72:         } catch ( InterruptedException x ) {
73:             // ignore
74:         } finally {
75:             // The thread is about to die, make sure that the
76:             // reference to it is also lost.
77:             runThread = null;
78:         }
79:     }
80: 
81:     public static void main(String[] args) {
82:         VisualSuspendResume vsr = new VisualSuspendResume();
83:         Thread t = new Thread(vsr);
84:         t.start();
85:
86:         JFrame f = new JFrame("Visual Suspend Resume");
87:         f.setContentPane(vsr);
88:         f.setSize(320, 200);
89:         f.setVisible(true);
90:         f.addWindowListener(new WindowAdapter() {
91:                 public void windowClosing(WindowEvent e) {
92:                     System.exit(0);
93:                 }
94:             });
95:     }
96: }

The VisualSuspendResume class subclasses JPanel and allows a thread to be run within it by implementing the Runnable interface (lines 5–7). A noneditable JTextField is created (lines 16–19) and used to sequentially display each character defined in the set of symbols (lines 9–10). The symbols are displayed in a continuous loop to create the illusion of a line spinning about its center in a clockwise rotation. A Suspend button is added that calls suspendNow() when clicked (lines 21 and 24–28). A Resume button is also added that calls resumeNow() when clicked (lines 22 and 30–34). These three components are stacked vertically in the panel (lines 36–43).

When a new thread enters run(), a reference to it is stored in runThread (line 62). Regardless of how the thread leaves the try block (lines 59–71), it will enter the finally block and set the runThread reference back to null (lines 74–78). This reference is used by suspendNow() and resumeNow() to control the thread.

As long as the thread is not suspended, it continues to execute the statements in the infinite while loop (lines 65–71). Each time through the loop, the next symbol is retrieved from the list, and the JTextField is updated through the setText() method (lines 67–68). After the thread sleeps for a bit (line 69), count is incremented (line 70) in preparation for retrieving the next symbol in the cycle.

When the Suspend button is clicked and the suspendNow() method is called, the thread pointed to by runThread is suspended (line 48). If runThread is not currently set, the request is simply ignored (line 47). Similarly, when the Resume button is clicked, resumeNow() is invoked and executes the resume() method of the Thread object (line 54) for the thread currently inside run().

Figure 5.1 shows a snapshot of how VisualSuspendResume looks when running.

A screen shot of VisualSuspendResume while running.

Figure 5.1. A screen shot of VisualSuspendResume while running.

When you run this code, notice how the spinner stops when Suspend is clicked and how it starts again when Resume is clicked. Also note that clicking Suspend several times in a row has no adverse effects. It does no harm to suspend a thread that is already suspended.Likewise, clicking Resume several times in a row is also fine. It does no harm to resume a thread that is currently running.

The suspend() method is deprecated as of JDK 1.2 because if a thread is suspended at an inopportune time—such as when it is holding a lock for a shared resource—a deadlock condition may result. I explain and demonstrate deadlocks in Chapter 7, "Concurrent Access to Objects and Variables," but let it suffice to say for now that a deadlock is a very bad thing and can cause a program to freeze up on a user. Even when locks are not involved, a thread might be suspended while in the middle of a long procedure that really should not be left in a partially completed state. The resume() method is deprecated because without any use of suspend(), it is not needed.

Tip

Although suspend() and resume() are not deprecated in JDK 1.0 and 1.1, they should not be used for the same good reasons that they are deprecated in JDK 1.2. Alternative techniques to very closely simulate and safely replace the functionality of suspend() and resume() are presented next.

Suspending at a Bad Time

Although suspending a thread while it is holding a lock on a shared resource can be disastrous, suspending it while it's in the middle of long-running computation can also lead to problems. The sample class DeprecatedSuspendResume, shown in Listing 5.7, slows things down with some sleeps to make a thread more likely to be suspended at an inopportune time.

Example 5.7. DeprecatedSuspendResume.java—An Example of Suspension at a Bad Time

 1: public class DeprecatedSuspendResume
 2:         extends Object
 3:         implements Runnable {
 4:
 5:     private volatile int firstVal;
 6:     private volatile int secondVal;
 7:
 8:     public boolean areValuesEqual() {
 9:         return ( firstVal == secondVal );
10:     }
11:
12:     public void run() {
13:         try {
14:             firstVal = 0;
15:             secondVal = 0;
16:             workMethod();
17:         } catch ( InterruptedException x ) {
18:             System.out.println(
19:                     "interrupted while in workMethod()");
20:         }
21:     }
22:
23:     private void workMethod() throws InterruptedException {
24:         int val = 1;
25:
26:         while ( true ) {
27:             stepOne(val);
28:             stepTwo(val);
29:             val++;
30:
31:             Thread.sleep(200);  // pause before looping again
32:         }
33:     }
34:
35:     private void stepOne(int newVal)
36:             throws InterruptedException {
37: 
38:         firstVal = newVal;
39:         Thread.sleep(300);  // simulate some other long process
40:     }
41:
42:     private void stepTwo(int newVal) {
43:         secondVal = newVal;
44:     }
45:
46:     public static void main(String[] args) {
47:         DeprecatedSuspendResume dsr =
48:                 new DeprecatedSuspendResume();
49:         Thread t = new Thread(dsr);
50:         t.start();
51:
52:         // let the other thread get going and run for a while
53:         try { Thread.sleep(1000); } 
54:         catch ( InterruptedException x ) { }
55:
56:         for ( int i = 0; i < 10; i++ ) {
57:             t.suspend();
58:             System.out.println("dsr.areValuesEqual()=" +
59:                                 dsr.areValuesEqual());
60:             t.resume();
61:             try { 
62:                 // Pause for a random amount of time
63:                 // between 0 and 2 seconds.
64:                 Thread.sleep(
65:                         ( long ) (Math.random() * 2000.0) );
66:             } catch ( InterruptedException x ) {
67:                 // ignore
68:             }
69:         }
70:
71:         System.exit(0); // abruptly terminate application
72:     }
73: }

In main(), a new DeprecatedSuspendResume object is instantiated and a new thread is spawned to run it (lines 47–50). The main thread pauses for a second to let the new thread get up and running (line 53).

The newly spawned thread begins execution in run() (line 12) and then enters a try/catch block that catches any InterruptedException that might be thrown (lines 13–20). After making sure that both values are initially set to zero (lines 14–15), run() invokes workMethod(). In workMethod(), val is initially set to 1, incremented each time through the while loop, and passed as a parameter to both stepOne() and stepTwo(). In stepOne() (lines 35–40), firstVal is assigned its new value (line 38), and then a sleep() is used to simulate some other task that takes some time to execute (line 39). In stepTwo() (lines 42–44), secondVal is assigned its new value, and then it immediately returns (line 43). Back in workMethod(), a sleep() is used to slow things down before looping again (line 31).

Trouble arises when the new thread is suspended after it has set firstVal, but before it has set secondVal. Meanwhile, the main thread enters the for loop (lines 56–69). Each time through, the new thread is suspended (line 57) to check whether firstVal and secondVal are equal (lines 58–59). After the check, the thread is allowed to resume execution (line 60). The main thread then sleeps for a random amount of time between 0.0 and 2.0 seconds (lines 61–68) before jumping back up to the for. After 10 iterations, System.exit() is used to terminate all threads and exit the VM (line 71).

When this DeprecatedSuspendResume is run, output something like the following sample should be produced (your output will differ somewhat):

dsr.areValuesEqual()=true
dsr.areValuesEqual()=false
dsr.areValuesEqual()=false
dsr.areValuesEqual()=false
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=false
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=false

Notice that the value returned from areValuesEqual() is sometimes true and sometimes false. It is false when the new thread is suspended some time after stepOne() is called and before stepTwo() is completed. This is a bad time for the suspension to occur, but it cannot be avoided because the thread has no control over when its suspend method is called.

Suspending and Resuming Without Deprecated Methods

The method suspend() may be invoked on a particular thread at any time. In most programs, there are typically times when a thread is holding a lock or is in the middle of a long-running calculation and should not be suspended. In fact, suspending a thread while it is holding a lock can easily lead to a deadlock condition. Also, the suspend() and resume() methods have been wisely deprecated in the most recent JDK. In spite of all these reasons not to use suspend(), there are still situations where a thread's activities should be temporarily suspended. The AlternateSuspendResume class shown in Listing 5.8 demonstrates another way to achieve this.

Example 5.8. AlternateSuspendResume.java—Avoiding the Use of suspend() and resume()

  1: public class AlternateSuspendResume
  2:         extends Object
  3:         implements Runnable {
  4:
  5:     private volatile int firstVal;
  6:     private volatile int secondVal;
  7:     private volatile boolean suspended;
  8:
  9:     public boolean areValuesEqual() {
 10:         return ( firstVal == secondVal );
 11:     }
 12:
 13:     public void run() {
 14:         try {
 15:             suspended = false;
 16:             firstVal = 0;
 17:             secondVal = 0;
 18:             workMethod();
 19:         } catch ( InterruptedException x ) {
 20:             System.out.println(
 21:                 "interrupted while in workMethod()");
 22:         }
 23:     }
 24:
 25:     private void workMethod() throws InterruptedException {
 26:         int val = 1;
 27:
 28:         while ( true ) {
 29:             // blocks only if suspended is true
 30:             waitWhileSuspended();
 31:
 32:             stepOne(val);
 33:             stepTwo(val);
 34:             val++;
 35:
 36:             // blocks only if suspended is true
 37:             waitWhileSuspended();
 38: 
 39:             Thread.sleep(200);  // pause before looping again
 40:         }
 41:     }
 42:
 43:     private void stepOne(int newVal)
 44:                     throws InterruptedException {
 45:
 46:         firstVal = newVal;
 47:
 48:         // simulate some other lengthy process
 49:         Thread.sleep(300); 
 50:     }
 51:
 52:     private void stepTwo(int newVal) {
 53:         secondVal = newVal;
 54:     }
 55:
 56:     public void suspendRequest() {
 57:         suspended = true;
 58:     }
 59:
 60:     public void resumeRequest() {
 61:         suspended = false;
 62:     }
 63:
 64:     private void waitWhileSuspended()
 65:                 throws InterruptedException {
 66:
 67:         // This is an example of a "busy wait" technique.  It is
 68:         // not the best way to wait for a condition to change
 69:         // because it continually requires some processor
 70:         // cycles to perform the checks.  A better technique
 71:         // is to use Java's built-in wait-notify mechanism.
 72:         while ( suspended ) {
 73:             Thread.sleep(200);
 74:         }
 75:     }
 76:
 77:     public static void main(String[] args) {
 78:         AlternateSuspendResume asr =
 79:                 new AlternateSuspendResume();
 80: 
 81:         Thread t = new Thread(asr);
 82:         t.start();
 83:
 84:         // let the other thread get going and run for a while
 85:         try { Thread.sleep(1000); } 
 86:         catch ( InterruptedException x ) { }
 87:
 88:         for ( int i = 0; i < 10; i++ ) {
 89:             asr.suspendRequest();
 90:
 91:             // Give the thread a chance to notice the
 92:             // suspension request.
 93:             try { Thread.sleep(350); } 
 94:             catch ( InterruptedException x ) { }
 95:
 96:             System.out.println("dsr.areValuesEqual()=" +
 97:                     asr.areValuesEqual());
 98:
 99:             asr.resumeRequest();
100:
101:             try {
102:                 // Pause for a random amount of time
103:                 // between 0 and 2 seconds.
104:                 Thread.sleep(
105:                         ( long ) (Math.random() * 2000.0) );
106:             } catch ( InterruptedException x ) {
107:                 // ignore
108:             }
109:         }
110:
111:         System.exit(0); // abruptly terminate application
112:     }
113: }

A new volatile member variable suspended is added (line 7) to keep track of a request to have the internal thread temporarily stop processing. This flag is initially false (line 15) in the beginning of run(). It is set to true every time the suspendRequest() method (lines 56–58) is invoked. It is set back to false every time resumeRequest() (lines 60–62) is invoked. The current value of suspended is checked in waitWhileSuspended() (lines 64–75), and if true, it will check (line 72) five times per second (every 200 milliseconds) to see whether the value has changed. Calls to waitWhileSuspended() do not return until suspended is false. If suspended was already false, calls return right away.

The loop inside waitWhileSuspended() is performing a busy wait. That is, the thread sleeps briefly between checks, but uses up processor cycles repeatedly performing these checks. If the sleep interval is reduced, even more processor resources are wasted. If the sleep interval is increased, it will be longer (on average) before it notices a change in the monitored value. (Later in this chapter, the class BestReplacement uses the wait/notify mechanism instead of a busy wait to provide the same functionality more efficiently.)

Tip

Busy waits are wasteful of processor resources and should be avoided. Using Java's wait/notify mechanism is a much better design decision in most cases. I'll show you how to use this mechanism in Chapter 8.

You can see that waitWhileSuspended() is invoked twice within the while loop of workMethod() (lines 30 and 37). Both these times, it would be all right for the thread to be suspended for a while. If it has not been suspended, these calls return right away.

Tip

If there is more than one safe place to suspend a thread in your code, you should add more waitWhileSuspended() method calls to all the safe places. Just make sure that you don't allow suspensions while holding a lock! Calls to waitWhileSuspended() should be done frequently to help the thread respond quickly to a suspend request. At the same time, keep in mind that invoking waitWhileSuspended() uses some processor resources, so don't use it too frequently. I'll provide you with a detailed explanation of locks in Chapter 7, "Concurrent Access to Objects and Variables."

In the main() method, instead of invoking suspend() on the Thread object, the suspendRequest() method is invoked on the AlternateSuspendResume object (line 89). As the name implies, this is only a request that the internal thread temporarily pause. It will continue to run until the next time that it calls waitWhileSuspended(), allowing any partially completed tasks to finish. After putting in the request, the main thread sleeps for 350 milliseconds (line 93) to allow the new thread enough time to get to its next waitWhileSuspended() call. The areValuesEqual() results are printed (lines 96–97), and then the resumeRequest() method is called (line 99) to signal the new thread that it can now return from waitWhileSuspended().

When AlternateSuspendResume is run, output looks like this (your output should match):

dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true
dsr.areValuesEqual()=true

The only way that it would not find values equal would be if suspendRequest() had been called by the main thread, and the internal thread had not yet called waitWhileSuspended(). The 350-millisecond sleep used before checking the values should be adequate in this case. If you need to be sure that the internal thread has entered waitWhileSuspended() before taking some sort of action, you could introduce another boolean member variable that is true only while the internal thread is inside waitWhileSuspended(). In the BestReplacement class at the end of this chapter, I will show you a way that this can be done.

This new way of suspending and resuming thread execution via requests is far safer than having a thread directly and abruptly suspended via the suspend() method of Thread.

Stopping a Thread

A thread is considered to be alive from just after the start() method of Thread is invoked until it dies. A thread may die by returning from run()—which is the case in the examples so far. A thread may also die when the stop() method of Thread is invoked. Like suspend() and resume(), stop() has been deprecated as of JDK 1.2.

Using the Deprecated Method stop()

At times, one of the additional threads spawned in a multithreaded program is no longer needed, but it continues to execute statements—perhaps in an infinite loop. The Thread API includes a stop() method to abruptly terminate a thread's execution:

public final void stop()

When this method is invoked on a thread, the thread immediately throws a java.lang.ThreadDeath error (a subclass of java.lang.Error, which itself is a subclass of java.lang.Throwable). This exception propagates up the method call stack, executing any finally clauses present and releasing any locks that are currently held. Ultimately, this exception causes run() to abruptly return, and the thread dies.

The class DeprecatedStop, in Listing 5.9, shows a very simple example of how to use stop().

Example 5.9. DeprecatedStop.java—An Example Using the Deprecated stop() Method

 1: public class DeprecatedStop extends Object implements Runnable {
 2:
 3:     public void run() {
 4:         int count = 0;
 5:
 6:         while ( true ) {
 7:             System.out.println("Running ... count=" + count);
 8:             count++;
 9:
10:             try {
11:                 Thread.sleep(300);
12:             } catch ( InterruptedException x ) {
13:                 // ignore
14:             }
15:         }
16:     }
17:
18:
19:     public static void main(String[] args) {
20:         DeprecatedStop ds = new DeprecatedStop();
21:         Thread t = new Thread(ds);
22:         t.start();
23:
24:         try {
25:             Thread.sleep(2000);
26:         } catch ( InterruptedException x ) {
27:             // ignore
28:         }
29:
30:         // Abruptly stop the other thread in its tracks!
31:         t.stop();
32:     }
33: }

The main thread creates a new DeprecatedStop object (line 20) and a new Thread object (line 21) and starts the thread running (line 22). It then sleeps for two seconds while the new thread runs.

The new thread runs an infinite while loop (lines 6–15). In this loop, it prints a message with the latest count (line 7), increments count, and then sleeps for 300 milliseconds. This is done until the thread is stopped.

Back in the main() method, the main thread wakes up after its 2-second nap and abruptly stops the new thread (line 31).

When run, it produces the following output (your output should match):

Running ... count=0
Running ... count=1
Running ... count=2
Running ... count=3
Running ... count=4
Running ... count=5
Running ... count=6

The stop() method is deprecated as of JDK 1.2 because it can lead to corrupted data in objects. One problem is that when a thread is stopped abruptly, there is little opportunity to perform any cleanup work. Another problem is that when stop() is invoked on a thread, the thread releases all the locks it is currently holding. The locks are definitely being held for a good reason—probably to keep other threads from accessing data elements that are not yet in a consistent state. This sudden release of the locks may leave some data in some objects in an inconsistent state with no warning about the possible corruption. In many cases, there are other ways to have a thread return from run() in an orderly manner that leaves all objects in a consistent state.

Tip

Although stop() is not deprecated in JDK 1.0 and 1.1, it should not be used for the same good reasons that it is deprecated in JDK 1.2. Alternative techniques to very closely simulate and safely replace the functionality of stop() are presented next.

An Alternative to stop()

As an alternative to directly stopping a thread, a boolean indicator variable can be used to determine whether the thread should continue processing. In conjunction with this, the interrupt() method can be used to signal the thread that it should take a different course of action. Earlier in this chapter, the class PiInterrupt (refer to Listing 5.5) uses interrupt() to signal the new thread that it should stop refining its calculation and bail out.

The class AlternateStop, in Listing 5.10, shows how interrupt() and a boolean indicator can be used together to get a thread to safely stop.

Example 5.10. AlternateStop.java—Using interrupt() and a boolean Flag to Safely Stop a Thread

 1: public class AlternateStop extends Object implements Runnable {
 2:     private volatile boolean stopRequested;
 3:     private Thread runThread;
 4:
 5:     public void run() {
 6:         runThread = Thread.currentThread();
 7:         stopRequested = false;
 8:
 9:         int count = 0;
10:
11:         while ( !stopRequested ) {
12:             System.out.println("Running ... count=" + count);
13:             count++;
14:
15:             try {
16:                 Thread.sleep(300);
17:             } catch ( InterruptedException x ) {
18:                 Thread.currentThread().interrupt(); // reassert
19:             }
20:         }
21:     }
22:
23:     public void stopRequest() {
24:         stopRequested = true;
25:
26:         if ( runThread != null ) {
27:             runThread.interrupt();
28:         }
29:     }
30:
31:     public static void main(String[] args) {
32:         AlternateStop as = new AlternateStop();
33:         Thread t = new Thread(as);
34:         t.start();
35: 
36:         try {
37:             Thread.sleep(2000);
38:         } catch ( InterruptedException x ) {
39:             // ignore
40:         }
41:
42:         as.stopRequest();
43:     }
44: }

In main(), a new thread is spawned (lines 32–34) to run inside an AlternateStop instance. The main thread then sleeps for two seconds to let the new thread run (lines 36–40).

The volatile member variable stopRequested (line 2) is used as the flag to indicate whether the thread should continue to run. The member variable runThread (line 3) is used to keep a reference to the thread that is currently running inside run().

When run() is invoked, the current thread is stored in runThread (line 6) for later use, and stopRequested is set to false. The while loop (lines 11–20) continues until stopRequested is true. Inside the while loop, any time a InterruptedException might be thrown, it should be caught (line 17) and reasserted (line 18). The interrupt flag should be reasserted in case any other statements yet to be executed in the loop have to be interrupted (in this example, it doesn't apply, but it's a good habit to get into). If there were more interruptible statements like sleep() or wait(), they would not throw an InterruptedException unless the interrupt had been reasserted. You do want them to throw an InterruptedException right away because the thread has been signaled to clean up and die.

Back in the main thread, after the two-second nap, the stopRequest() method of AlternateStop is invoked (line 42) to put in a request that the new thread die as soon as possible. When stopRequest() is invoked, stopRequested is set to true (line 24), and the thread that is running inside run() is interrupted (line 27).

When run, it produces the following output (your output should match):

Running ... count=0
Running ... count=1
Running ... count=2
Running ... count=3
Running ... count=4
Running ... count=5
Running ... count=6

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

The class 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 stop(), suspend(), and resume().

Example 5.11. BestReplacement.java—Combined Alternative Techniques

  1: // uses BooleanLock from chapter 17
  2:
  3: public class BestReplacement extends Object {
  4:     private Thread internalThread;
  5:     private volatile boolean stopRequested;
  6:
  7:     private BooleanLock suspendRequested;
  8:     private BooleanLock internalThreadSuspended;
  9:
 10:     public BestReplacement() {
 11:         stopRequested = false;
 12:
 13:         suspendRequested = new BooleanLock(false);
 14:         internalThreadSuspended = new BooleanLock(false);
 15:
 16:         Runnable r = new Runnable() {
 17:                 public void run() {
 18:                     try {
 19:                         runWork();
 20:                     } catch ( Exception x ) {
 21:                         // in case ANY exception slips through
 22:                         x.printStackTrace();
 23:                     }
 24:                 }
 25:             };
 26:
 27:         internalThread = new Thread(r);
 28:         internalThread.start();
 29:     }
 30:
 31:     private void runWork() {
 32:         int count = 0;
 33: 
 34:         while ( !stopRequested ) {
 35:             try {
 36:                 waitWhileSuspended();
 37:             } catch ( InterruptedException x ) {
 38:                 // Reassert interrupt so that remaining code
 39:                 // sees that an interrupt has been requested.
 40:                 Thread.currentThread().interrupt();
 41:
 42:                 // Reevaluate while condition --probably
 43:                 // false now.
 44:                 continue;
 45:             }
 46:
 47:             System.out.println("Part I - count=" + count);
 48:
 49:             try {
 50:                 Thread.sleep(1000);
 51:             } catch ( InterruptedException x ) {
 52:                 Thread.currentThread().interrupt(); // reassert
 53:                 // continue on as if sleep completed normally
 54:             }
 55:
 56:             System.out.println("Part II - count=" + count);
 57:
 58:             try {
 59:                 Thread.sleep(1000);
 60:             } catch ( InterruptedException x ) {
 61:                 Thread.currentThread().interrupt(); // reassert
 62:                 // continue on as if sleep completed normally
 63:             }
 64:
 65:             System.out.println("Part III - count=" + count);
 66:
 67:             count++;
 68:         }
 69:     }
 70:
 71:     private void waitWhileSuspended()
 72:                     throws InterruptedException {
 73:
 74:         // only called by the internal thread - private method
 75:
 76:         synchronized ( suspendRequested ) {
 77:             if ( suspendRequested.isTrue() ) {
 78:                 try {
 79:                     internalThreadSuspended.setValue(true);
 80:                     suspendRequested.waitUntilFalse(0);
 81:                 } finally {
 82:                     internalThreadSuspended.setValue(false);
 83:                 }
 84:             }
 85:         }
 86:     }
 87:
 88:     public void suspendRequest() {
 89:         suspendRequested.setValue(true);
 90:     }
 91:
 92:     public void resumeRequest() {
 93:         suspendRequested.setValue(false);
 94:     }
 95:
 96:     public boolean waitForActualSuspension(long msTimeout)
 97:                     throws InterruptedException {
 98:
 99:         // Returns 'true'if suspended, 'false'if the
100:         // timeout expired.
101:
102:         return internalThreadSuspended.waitUntilTrue(msTimeout);
103:     }
104:
105:     public void stopRequest() {
106:         stopRequested = true;
107:         internalThread.interrupt();
108:     }
109:
110:     public boolean isAlive() {
111:         return internalThread.isAlive();
112:     }
113:
114:     public static void main(String[] args) {
115:         try {
116:             BestReplacement br = new BestReplacement();
117:             System.out.println(
118:                     "--> just created, br.isAlive()=" +
119:                     br.isAlive());
120:             Thread.sleep(4200);
121: 
122:             long startTime = System.currentTimeMillis();
123:             br.suspendRequest();
124:             System.out.println(
125:                     "--> just submitted a suspendRequest");
126:
127:             boolean suspensionTookEffect =
128:                     br.waitForActualSuspension(10000);
129:             long stopTime = System.currentTimeMillis();
130:
131:             if ( suspensionTookEffect ) {
132:                 System.out.println(
133:                     "--> the internal thread took " +
134:                     (stopTime - startTime) + " ms to notice " +
135:                     "
    the suspend request and is now " +
136:                     "suspended.");
137:             } else {
138:                 System.out.println(
139:                     "--> the internal thread did not notice " +
140:                     "the suspend request " +
141:                     "
    within 10 seconds.");
142:             }
143:
144:             Thread.sleep(5000);
145:
146:             br.resumeRequest();
147:             System.out.println(
148:                     "--> just submitted a resumeRequest");
149:             Thread.sleep(2200);
150:
151:             br.stopRequest();
152:             System.out.println(
153:                     "--> just submitted a stopRequest");
154:         } catch ( InterruptedException x ) {
155:             // ignore
156:         }
157:     }
158: }

In writing BestReplacement, I used some techniques from other chapters. It is a self-running object (for more details, see the technique in 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 private member variable (line 4). The Runnable used by the internal thread is an inner class (lines 16–25). By using an inner class, the public void run() method is hidden and cannot be erroneously called by external code. Within this inner class, the private void runWork() 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 internalThread.start() operation to be performed when appropriate.

Additionally, the BooleanLock class is used from Chapter 17. BooleanLock encapsulates the wait/notify mechanism inside a class that holds a boolean value. The 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 waitUntilTrue() and waitUntilFalse(). In BestReplacement, two instances of BooleanLock are used. The suspendRequested instance (line 7) is used to track whether a suspend has been requested. The internalThreadSuspended instance (line 8) is used to determine if the internal thread has noticed a suspend request. Both are initially set to false (lines 13–14).

Inside the runWork() method, the while loop (lines 34–68) continues until stopRequested is true. Each time through, waitWhileSuspended() 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 InterruptedException. 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 while because of the continue statement (line 44).

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).

In suspendRequest(), the suspendRequested instance of BooleanLock has its value set to true (line 89). In resumeRequest(), suspendRequest is set to false (line 93). All of the synchronization and notification necessary for changing the value is encapsulated inside BooleanLock.

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

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

Tip

Don't worry if you don't fully understand the use of synchronized and the wait/notify mechanism encapsulated in BooleanLock at this time. I fully explain them in Chapters 7, 8, and 17.

The internal thread can be stopped by invoking stopRequest(). This method first sets the stopRequest boolean flag to true (line 106). It then interrupts the internal thread to "unstick" it from any blocking sleeps or waits (line 107). The isAlive() method is used to check whether the internal thread has died (line 111).

When run, output such as the following is produced:

--> just created, br.isAlive()=true
Part I - count=0
Part II - count=0
Part III - count=0
Part I - count=1
Part II - count=1
Part III - count=1
Part I - count=2
--> just submitted a suspendRequest
Part II - count=2
Part III - count=2
--> the internal thread took 1810 ms to notice
    the suspend request and is now suspended.
--> just submitted a resumeRequest
Part I - count=3
Part II - count=3
Part III - count=3
Part I - count=4
--> just submitted a stopRequest
Part II - count=4
Part III - count=4

Notice that when a suspend request is submitted, the loop continues until the suspend check at the top (waitWhileSuspended()). Also notice that when stopped, the internal thread does not immediately terminate, but instead finishes its tasks in an orderly manner.

Caution

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 read() method on an InputStream blocks until a new byte is available, the end of stream is reached, or an IOException is thrown. It does not throw an InterruptedException if the blocked thread is interrupted! A technique for dealing with this situation is offered in Chapter 15, "Breaking Out of a Blocked I/O State."

Daemon Threads

Threads that are marked as daemons stop in a whole new way. Daemon threads are used for background supporting tasks and are only needed while normal, nondaemon threads are still running. When the VM detects that the only remaining threads are daemon threads, it exits. If any nondaemon thread is still alive, the VM will not exit. Daemon threads provide a nice way of managing some sort of behind-the-scenes processing that is only necessary to support other nondaemon threads.

The classes DaemonThread (see Listing 5.12) and DaemonThreadMain (see Listing 5.13) show how a daemon thread behaves.

Example 5.12. DaemonThread.java—The Daemon Thread Example

 1: public class DaemonThread extends Object implements Runnable {
 2:     public void run() {
 3:         System.out.println("entering run()");
 4:
 5:         try {
 6:             System.out.println("in run() - currentThread()=" +
 7:                     Thread.currentThread());
 8:
 9:             while ( true ) {
10:                 try { Thread.sleep(500); } 
11:                 catch ( InterruptedException x ) { }
12:
13:                 System.out.println("in run() - woke up again");
14:             }
15:         } finally {
16:             System.out.println("leaving run()");
17:         }
18:     }
19: }

In this simple example, DaemonThread has only one method: run(). The message entering run() is printed when a thread begins (line 3). The while loop (lines 9–14) runs indefinitely, printing the woke up again message twice each second.

Example 5.13. DaemonThreadMain.java—The Entry Point for the Daemon Thread Example

 1: public class DaemonThreadMain extends Object {
 2:     public static void main(String[] args) {
 3:         System.out.println("entering main()");
 4:
 5:         Thread t = new Thread(new DaemonThread());
 6:         t.setDaemon(true);
 7:         t.start();
 8:
 9:         try { Thread.sleep(3000); } catch ( InterruptedException x ) { }
10:
11:         System.out.println("leaving main()");
12:     }
13: }

In DaemonThreadMain, a new DaemonThread object is created and passed to one of Thread's constructors (line 5). Before the new thread is started, the daemon flag is set to true (line 6) to indicate that this thread should not keep the VM from exiting. Next, the thread is started (line 7). After letting the daemon thread run for three seconds (line 9), the main thread simply falls out of main() and dies. Very soon after this, the VM notices that the other thread is only a daemon thread and exits.

When DaemonThreadMain is run, something close to the following should be produced (your output might differ):

entering main()
entering run()
in run() - currentThread()=Thread[Thread-0,5,main]
in run() - woke up again
in run() - woke up again
in run() - woke up again
in run() - woke up again
in run() - woke up again
leaving main()
in run() - woke up again

One last woke up again message might be printed after the leaving main message. If the new thread was not a daemon thread, the woke up again messages would continue to be produced until the VM was terminated manually. Also notice that the daemon thread was abruptly stopped and never printed the message in its finally clause.

Caution

Although daemon threads can be very useful, care must be taken to ensure that it's not harmful for them to be stopped in their tracks when all the other nondaemon threads have died.

Summary

In this chapter, I showed you several ways to temporarily and permanently stop a thread's execution. Some of the lessons learned are

  • interrupt() can be used to set a thread's interrupted status to true and break a thread out of a blocked sleeping or waiting state.

  • InterruptedException is thrown to indicate that a thread has noticed that it has been interrupted.

  • isInterrupted() can be used at any time on any thread to check whether it has been interrupted, without altering the interrupted status.

  • Thread.interrupted() can be used to check whether the current thread has been interrupted, and if so, it automatically resets the interrupted status flag back to false.

  • suspend(), resume(), and stop() are deprecated as of JDK 1.2 and should also be avoided in earlier releases.

  • Alternative techniques are available to replace the functionality of the deprecated methods.

  • Daemon threads automatically stop when all the nondaemon threads have died.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.49.252