This is part 4 of this series. If you missed any of the last three, you can find them here:
In this post, we’ll look at how to terminate a thread gracefully and interrupting threads.
Terminating Threads
We’ve already seen an example of the run()
method in the previous post:
@Override
public void run() {
while (true) {
statements;
}
}
We’ve seen that there is no way to stop this thread other than to set it to be a daemon thread, which will be stopped when the JVM closes.
But what happens if we have a long-running application, and the JVM doesn’t close?
We can call the deprecated stop()
method of the Thread
class. But this has all sorts of serious ramifications when we’re dealing with synchronized methods holding monitors, and can lead to data integrity problems. We’ll discuss these issues in detail in later posts.
We should rather have a boolean
flag in the run()
method to check whether the thread should keep running, as follows:
@Override
public void run() {
while (moreWorkToDo) {
statements;
}
}
By setting the boolean
flag to false
we can end the loop and allow the thread to terminate gracefully .
Interrupting Threads
A thread should not work continuously in its run()
method. It should sleep()
or wait()
occasionally to allow other threads an opportunity to run. The sleep()
, wait()
and join()
methods are blocking methods. When a thread is blocked, however, it cannot check the boolean
flag to see whether it should end.
The interrupt()
method can be called to interrupt these blocking methods. The thread needs to call the interrupted()
method to check whether it has been interrupted. When the interrupt()
method is called on a thread that is blocked, the blocking method is terminated by an InterruptedException
. The InterruptedException
can then be used as a request for termination.
The following example shows how the InterruptedException
and interrupted()
method can be used in the run()
method:
public void run() {
try {
while (!interrupted() && moreWorkToDo) {
statements; // with one or more blocking methods
}
}
catch (InterruptedException e) {
// thread was interrupted during a blocking method
// we can generally safely ignore this
}
finally {
// clean up code if necessary
}
// thread can exit and terminate
} // end of run()
Example
Let’s take the example code from last post, and strip some lines from it:
public class InterruptedThreadTest {
public static void main(String args[]) {
Thread task = new Thread (
// anonymous inner class
new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("Hello " + threadName);
while (true) {
System.out.print('.');
}
}
}
);
// main thread prints its default name
System.out.println("Hello " + Thread.currentThread().getName());
// background task runs in parallel as a separate thread
task.setDaemon(true);
task.start();
System.out.println("Done!");
} // end of main
} // end of class
If the background thread task
is a daemon thread, it ends when the main thread ends. If we change the task.setDaemon(true)
call to false
, the background thread never ends, and prints forever.
Let’s try to interrupt the task before the "Done!"
line by adding an interrupt()
call as follows:
task.interrupt();
System.out.println("Done!");
Nothing happens! The background thread still spins forever in its loop! What went wrong?
We all know someone who talks incessantly, and no matter how much we try to interrupt them, they just carry on talking. This background task is like that: it doesn’t listen to anyone trying to get a word in.
Let’s now modify the code to include the interrupted()
check. We’ll change the while(true)
line to while (!Thread.interrupted())
. We’ll also put the main thread to sleep for a while so that the background task has a bit of time to do its work:
public class InterruptedThreadTest {
public static void main(String args[]) throws Exception {
Thread task = new Thread (
// anonymous inner class
new Runnable() {
@Override
public void run() {
String threadName = Thread.currentThread().getName();
System.out.println("Hello " + threadName);
while (!Thread.interrupted()) {
System.out.print('.');
}
}
}
);
// main thread prints its default name
System.out.println("Hello " + Thread.currentThread().getName());
// background task runs in parallel as a separate thread
task.setDaemon(false);
task.start();
// main thread sleeps for 10 milliseconds to give task time to run
// I've cheated by not catching InterruptedException here, but
// have declared main() to throw it to keep the code clear.
Thread.sleep(10);
// interrupting the background task
task.interrupt();
System.out.println("Done!");
} // end of main
} // end of class
Now the background task runs as before, but checks in the loop condition whether it’s been interrupted. If it has been interrupted, it ends gracefully.
We didn’t use the moreWorkToDo
flag here as recommended earlier, i.e:
while (!interrupted() && moreWorkToDo)
but it’s the work of a moment for a sterling programmer to include it and test it.
Conclusion
In the next post we’ll look at the use of the synchronized
keyword, the thread monitor model, and the wait()
and notify()
methods of the Object
class.
Please share your comments.
Until then, stay safe and keep coding!