2002-09-16 The Java Specialists' Newsletter [Issue 056] - Shutting down threads cleanlyAuthor: Dr. Heinz M. KabutzIf 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 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
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 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 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 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 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
If you want to stop a thread waiting on a socket, you will have to
unfortunately close the socket underneath the thread. Fortunately,
the 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
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 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 |
|
来自: shaobin0604@1... > 《Android》