风象星座和水象星座:Shutting down threads cleanly,java tutorial,java tutorials

来源:百度文库 编辑:九乡新闻网 时间:2024/04/28 00:35:21

2002-09-16 The Java Specialists' Newsletter [Issue 056] - Shutting down threads cleanly

Author:Dr. Heinz M. Kabutz

If you are reading this, and have not subscribed, please consider doing it now by going to oursubscribepage. You can subscribe either via email or RSS.

Welcome to the 56th edition of The Java(tm) Specialists' Newsletter sent to 4609 JavaSpecialists in 85countries. Whenever I think I wrote a "killer" newsletter,I get a lukewarm response from my readers, and when I think Iwrote a flop, I get accolades, like with my last newsletter about Oak.I expected rotten tomatoes in the form of "here are 29 othernewsletters about Oak and modern Java". Perhaps I should try andwrite flops, then I will have more successes ;-)

When I was in Germany from June to August 2000, we lived in a villageabout 45 minutes by train from InforAG. Every day I had 1.5 hours to read books about varioustopics. Over the course of 3 months, I got through a lot of reading,I even read The Java Virtual MachineSpecification! Much like the Oakspecification, it is a *must* for any serious Java developer to read. The moreI 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 isConcurrent Programming in Java, 2nd Edby Doug Lea, one of the writers of java.util.HashMap.My thoughts on shutting down threads is based on ideas I gleaned fromthose two books, so before you shoot me down, please read the books.The Java 2 Performance and Idiom Guideis also an excellent book to read for the reason that it got methinking about performance the way that no other book has. It isquite dated, which cannot be avoided with a book about a language thatchanges so quickly. That book also agrees with my approach ofshutting down threads.

My September Design Patterns Course starting tomorrow is completelyfull. If you missed the September course, pleasesee the end of this email for information about our upcomingNovember Design Patterns Course. That courseis mainly aimed at South Africans, due to the difficulty of gettinginto this country - a visa from Germany can take three weeks to getapproved! We do not want you to know how fantastic it is here, sowe make it as difficult as possible for you to get here, and especiallyto stay here. If you thought getting into the USA was hard,try South Africa.

All is not lost, however. If you live in a country far far away fromSouth Africa and your company would benefit from a solid DesignPatterns Course (which company would not?!?), I will gladlygive you a quote for me to come to you and presentthe course at your company. Simply sendme an email and you will hear from me personally within 24 hours.

How to shutdown threads cleanly

I remember starting off with JDK 1.0, and playing with Threads. I wouldstart a Thread, and then to stop it, I simply called stop().It was the most obvious thing to do, with disastrous consequences. If werefer back to the OakNewsletter, that was the same as having all your codeunprotected, and receiving an asynchronous exception in yourThread. The exception that is thrown asynchronously isjava.lang.ThreadDeath. Needless to say, the more Ithink about it, the more I am baffled that they allowed that inthe first place, especially in the absence of the protectkeyword that they had in Oak. Using stop() is incrediblydangerous, as it will kill your thread even if it is in the middle ofsomething important. There is no way to protect yourself, so if youspot code that uses stop(), you should frown.

So, how do you shutdown a thread cleanly? The developers of Java haveactually left the protect mechanism in place for us to use, it is justnamed differently. First of all, we should never use stop().Ever. Period. Do not even think of using it. Sun should remove itfrom java.lang.Thread as soon as possible. Don't. No.Noooooo. Doooown. Secondly, the only place where we are allowed toreceive an exception to tell us that the thread is being shutdown iswhile the thread is blocked. Getting a shutdown notification at anyother time would be dangerous and nondeterministic. The way this worksis via the java.lang.InterruptedException. I admit thatInterruptedException is not the most obvious choice ofname for indicating that another thread is trying to shutdown yourthread.

I have seen code that uses a boolean flag to indicate whether the threadis running or not. However, Java already provides that flag in theform of the interrupted flag, so why duplicate effort? Usually, the codewould work something like this:

public class UsingFlagToShutdownThread extends Thread {private 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 thelongest we would wait unnecessarily would be one second. However if wenormally sleep for 30 seconds, then it could take a while before yourprogram is completely shut down. This is especially true if you havea lot of threads and you join() each one to make sure thatit does finish.

Java has another mechanism that you should rather use: simply interruptthe 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 importantbreak;}}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 InterruptedExceptionscorrectly, i.e. using my way ;-) Most of the time, programmers view InterruptedException as anirritating checked exception that they have to catch, but which they usuallyignore:

while (true) {// ... do somethingtry {Thread.sleep(30000);} catch (InterruptedException ex) {}}

Why do we have to interrupt the thread again?

In my example, after I caught the InterruptedException, Iused Thread.currentThread().interrupt() to immediatelyinterrupted the thread again. Why is this necessary? When the exceptionis 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 loopShut down outer loopShutting down threadShutdown the thread correctly.##########.##########.##########.######Shut down inner loop.##########.##########.##########.##########.##########.  etc.

Herein lies the danger with this approach: if some libraryincorrectly handles InterruptedException then yourcode will not shut down correctly.

From a purely theoretical view, you should use the interruptmechanism of threads to shut them down. However, you have tobe very careful that you use that mechanism throughout yourcode, otherwise you will not be able to shut down all yourthreads.

What about threads blocked on IO?

Threads can be blocked on wait(), sleep(),waiting to enter a synchronized block or waiting on some IO tocomplete. We cannot shut down a thread waiting to enter a synchronizedblock, so if you have a livelock or deadlock you will not be able toshut down your system cleanly. wait() and sleep()both throw an InterruptedException, as does join(). But, whatabout when you're blocked on IO? There is an exception calledjava.io.InterruptedIOException, which is supposed to coverthe situation where you interrupt a thread that is waiting on someIO to complete. As you might have guessed, it is not implementedconsistently. It works for piped streams, but none of the others seemto have that effect.

If you want to stop a thread waiting on a socket, you will have tounfortunately close the socket underneath the thread. Fortunately,the interrupt() method is not final, so youcan override it to also close the socket. Inside thecatch clause of java.io.IOExceptionyou 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 somethinglike 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 socketReading from input streamInterruptedShutting 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 streamInterrupted via InterruptedIOExceptionShutting down thread

Unfortunately, the IO library in Java is not consistent, soyou have to cater for both possibilities in your shutdownmethods.

I hope that this newsletter will be as useful to you as it hasbeen to me. Shutting down threads cleanly is unfortunately notas easy as it should be, but the mechanism in this newsletter issuperior to calling stop() (and thereby using anasynchronous exception) and it is also better than usinga flag to indicate whether the thread is supposed to carry onrunning or not.

The only problem with my approach is that if you use some librarythat does not handle InterruptedException correctly, you willhave problems shutting down your thread. You might have to havea separate thread that calls join() with a timeoutand repeatedly interrupts the thread until it is shut down.

That's the end of the newsletter. The birds are singing tocelebrate spring, my baby sister is here to visit, so we are nowgoing to celebrate life with a Cuban cigar :-)

Heinz