分享

Shutting down threads cleanly,java tutorial,java tutorials

 shaobin0604@163.com 2011-09-19

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 our subscribe page. You can subscribe either via email or RSS.

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 get 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 ;-)

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

My September Design Patterns Course starting tomorrow is completely full. If you missed the September course, please see the end of this email for information about our upcoming November Design Patterns Course. That course is mainly aimed at South Africans, due to the difficulty of getting into this country - a visa from Germany can take three weeks to get approved! We do not want you to know how fantastic it is here, so we make it as difficult as possible for you to get here, and especially to 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 from South Africa and your company would benefit from a solid Design Patterns Course (which company would not?!?), I will gladly give you a quote for me to come to you and present the course at your company. Simply send me 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 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 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) {}
}

Why do we have to interrupt the thread again?

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.

What about threads blocked on IO?

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

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多