Abstract: Starting threads is easy. Shutting them down again in an orderly fashion is not. In this newsletter we explore how to use interrupts to indicate to a thread that it should cooperatively shut down.
Welcome to the 56th edition of The Java(tm) Specialists' Newsletter sent to 4609 Java Specialists in 85 countries. Whenever I think I wrote a "killer" newsletter, I receive a lukewarm response from my readers, and when I think I wrote a flop, I get accolades, like with my last newsletter about Oak. I expected rotten tomatoes in the form of "here are 29 other newsletters about Oak and modern Java". Perhaps I should try and write flops, then I will have more successes ;-) [Update - that newsletter about Oak even made it into the Wikipedia page on Java - and I didn't put it there! But it's not in my top 10. This one is, surprisingly.]
When I was in Germany from June to August 2000, we lived in a village
about 45 minutes by train from Infor
AG. Every day I had 1.5 hours to read books about various
topics. Over the course of 3 months, I got through a lot of reading,
I even read The Java Virtual Machine
Specification [ISBN 0201432943]
! Much like the Oak
specification, it is a *must* for any serious Java developer to read. The more
I read in that spec, the more I realised that the hope of "write once,
run anywhere" was a wishful dream. Another book that I devoured is
Concurrent Programming in Java, 2nd Ed [ISBN 0201310090]
by Doug Lea, one of the writers of java.util.HashMap
.
My thoughts on shutting down threads is based on ideas I gleaned from
those two books, so before you shoot me down, please read the books.
The Java 2 Performance and Idiom Guide [ISBN 0130142603]
is also an excellent book to read for the reason that it got me
thinking about performance the way that no other book has. It is
quite dated, which cannot be avoided with a book about a language that
changes so quickly. That book also agrees with my approach of
shutting down threads.
javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.
I remember starting off with JDK 1.0, and playing with Threads. I would
start a Thread, and then to stop it, I simply called stop()
.
It was the most obvious thing to do, with disastrous consequences. If we
refer back to the Oak Newsletter,
that was the same as having all your code
unprotected, and receiving an asynchronous exception in your
Thread. The exception that is thrown asynchronously is
java.lang.ThreadDeath
. Needless to say, the more I
think about it, the more I am baffled that they allowed that in
the first place, especially in the absence of the protect
keyword that they had in Oak. Using stop()
is incredibly
dangerous, as it will kill your thread even if it is in the middle of
something important. There is no way to protect yourself, so if you
spot code that uses stop()
, you should frown.
So, how do you shutdown a thread cleanly? The developers of Java have
actually left the protect mechanism in place for us to use, it is just
named differently. First of all, we should never use stop()
.
Ever. Period. Do not even think of using it. Sun should remove it
from java.lang.Thread
as soon as possible. Don't. No.
Noooooo. Doooown. Secondly, the only place where we are allowed to
receive an exception to tell us that the thread is being shutdown is
while the thread is blocked. Getting a shutdown notification at any
other time would be dangerous and nondeterministic. The way this works
is via the java.lang.InterruptedException
. I admit that
InterruptedException
is not the most obvious choice of
name for indicating that another thread is trying to shutdown your
thread.
I have seen code that uses a boolean flag to indicate whether the thread is running or not. However, Java already provides that flag in the form of the interrupted flag, so why duplicate effort? Usually, the code would work something like this:
public class UsingFlagToShutdownThread extends Thread { private volatile boolean running = true; public void run() { while (running) { System.out.print("."); System.out.flush(); try { Thread.sleep(1000); } catch (InterruptedException ex) {} } System.out.println("Shutting down thread"); } public void shutdown() { running = false; } public static void main(String[] args) throws InterruptedException { UsingFlagToShutdownThread t = new UsingFlagToShutdownThread(); t.start(); Thread.sleep(5000); t.shutdown(); } }
What is so bad with that code? This example is not too bad, since the
longest we would wait unnecessarily would be one second. However if we
normally sleep for 30 seconds, then it could take a while before your
program is completely shut down. This is especially true if you have
a lot of threads and you join()
each one to make sure that
it does finish.
Java has another mechanism that you should rather use: simply interrupt the thread. The code would then look like this:
public class UsingInterruptToShutdownThread extends Thread { public void run() { while (true) { System.out.print("."); System.out.flush(); try { Thread.sleep(1000); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); // very important break; } } System.out.println("Shutting down thread"); } public static void main(String[] args) throws InterruptedException { Thread t = new UsingInterruptToShutdownThread(); t.start(); Thread.sleep(5000); t.interrupt(); } }
I must admit that I have not seen many programmers handle InterruptedExceptions correctly, i.e. using my way ;-) Most of the time, programmers view InterruptedException as an irritating checked exception that they have to catch, but which they usually ignore:
while (true) { // ... do something try { Thread.sleep(30000); } catch (InterruptedException ex) {} }
In my example, after I caught the InterruptedException
, I
used Thread.currentThread().interrupt()
to immediately
interrupted the thread again. Why is this necessary? When the exception
is thrown, the interrupted flag is cleared, so if you have nested loops,
you will cause trouble in the outer loops. Consider the following code:
public class NestedLoops extends Thread { private static boolean correct = true; public void run() { while (true) { System.out.print("."); System.out.flush(); for (int i = 0; i < 10; i++) { System.out.print("#"); System.out.flush(); try { Thread.sleep(100); } catch (InterruptedException ex) { if (correct) Thread.currentThread().interrupt(); System.out.println(); System.out.println("Shut down inner loop"); break; } } try { Thread.sleep(1000); } catch (InterruptedException ex) { if (correct) Thread.currentThread().interrupt(); System.out.println(); System.out.println("Shut down outer loop"); break; } } System.out.println("Shutting down thread"); } private static void test() throws InterruptedException { Thread t = new NestedLoops(); t.start(); Thread.sleep(6500); t.interrupt(); t.join(); System.out.println("Shutdown the thread correctly"); } public static void main(String[] args) throws InterruptedException { test(); correct = false; test(); } }
When you run this code, you will see something like this:
.##########.##########.##########.###### Shut down inner loop Shut down outer loop Shutting down thread Shutdown the thread correctly .##########.##########.##########.###### Shut down inner loop .##########.##########.##########.##########.##########. etc.
Herein lies the danger with this approach: if some library
incorrectly handles InterruptedException
then your
code will not shut down correctly.
From a purely theoretical view, you should use the interrupt mechanism of threads to shut them down. However, you have to be very careful that you use that mechanism throughout your code, otherwise you will not be able to shut down all your threads.
Threads can be blocked on wait()
, sleep()
,
waiting to enter a synchronized block or waiting on some IO to
complete. We cannot shut down a thread waiting to enter a synchronized
block, so if you have a livelock or deadlock you will not be able to
shut down your system cleanly. wait()
and sleep()
both throw an InterruptedException, as does join()
. But, what
about when you're blocked on IO? There is an exception called
java.io.InterruptedIOException
, which is supposed to cover
the situation where you interrupt a thread that is waiting on some
IO to complete. As you might have guessed, it is not implemented
consistently. It works for piped streams, but none of the others seem
to have that effect.
If you want to stop a thread waiting on a socket, you will have to
unfortunately close the socket underneath the thread. Fortunately,
the interrupt()
method is not final
, so you
can override it to also close the socket. Inside the
catch
clause of java.io.IOException
you can then check whether the thread has been interrupted or not:
import java.io.IOException; import java.io.InputStream; import java.io.InterruptedIOException; public class BlockedOnIO extends Thread { private final InputStream in; public BlockedOnIO(InputStream in) { this.in = in; } public void interrupt() { super.interrupt(); try { in.close(); } catch (IOException e) {} // quietly close } public void run() { try { System.out.println("Reading from input stream"); in.read(); System.out.println("Finished reading"); } catch (InterruptedIOException e) { Thread.currentThread().interrupt(); System.out.println("Interrupted via InterruptedIOException"); } catch (IOException e) { if (!isInterrupted()) { e.printStackTrace(); } else { System.out.println("Interrupted"); } } System.out.println("Shutting down thread"); } }
For shutting down threads reading from sockets, we would do something like this:
import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class BlockedOnSocketIO { public static void main(String[] args) throws IOException, InterruptedException { ServerSocket ss = new ServerSocket(4444); Socket socket = new Socket("localhost", 4444); System.out.println("Made socket, now reading from socket"); Thread t = new BlockedOnIO(socket.getInputStream()); t.start(); Thread.sleep(5000); t.interrupt(); } }
When we run our code, we see the following:
Made socket, now reading from socket Reading from input stream Interrupted Shutting down thread
Alternatively, when we use Pipes:
import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class BlockedOnPipedIO { public static void main(String[] args) throws IOException, InterruptedException { PipedInputStream in = new PipedInputStream(new PipedOutputStream()); Thread t = new BlockedOnIO(in); t.start(); Thread.sleep(5000); t.interrupt(); } }
When we run that code, we see the following:
Reading from input stream Interrupted via InterruptedIOException Shutting down thread
Unfortunately, the IO library in Java is not consistent, so you have to cater for both possibilities in your shutdown methods.
I hope that this newsletter will be as useful to you as it has
been to me. Shutting down threads cleanly is unfortunately not
as easy as it should be, but the mechanism in this newsletter is
superior to calling stop()
(and thereby using an
asynchronous exception) and it is also better than using
a flag to indicate whether the thread is supposed to carry on
running or not.
The only problem with my approach is that if you use some library
that does not handle InterruptedException correctly, you will
have problems shutting down your thread. You might have to have
a separate thread that calls join()
with a timeout
and repeatedly interrupts the thread until it is shut down.
That's the end of the newsletter. The birds are singing to celebrate spring, my baby sister is here to visit, so we are now going to celebrate life with a Cuban cigar :-)
Heinz
We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)
We deliver relevant courses, by top Java developers to produce more resourceful and efficient programmers within their organisations.