4

I've just run into an interesting issue. It seems that if, in Java, a thread calls System.exit() it cannot then be joined via Thread.join().

This is causing me issues as I want to use a shutdown hook to clean up after my application, such as:

Runtime.getRuntime.addShutdownHook(new Thread() {
    public void run() {
        reader.join();
        writer.join();
        in.close();
        out.close();
    }
});

The idea is that it ensures that the threads have finished with their respective resources, before closing those resources. The problem is that there a 3 situations by which the shutdown hook can be invoked. They are:

  1. User hits [ctrl] + [C].
  2. Reader thread completes, and calls System.exit().
  3. Writer thread completes, and calls System.exit().

The first case, where the user hits [ctrl] + [C] works fine. But in either of the other 2 cases, the shutdown hook blocks forever. This is a knock-on effect of the fact that one Thread.join(), which is called against a thread having already called System.exit(), blocks forever.

Thus, I have 2 questions. Firstly, I know that I could use Thread.join(long millis) instead so that they won't block indefinitely, but can anyone think of a more elegant solution? Secondly, while one can call Thread.join() against the same thread twice and on the second occasion it will simply return immediately, does anyone know of a reason why calling Thread.join() against a thread that has already called System.exit() blocks indefinitely and doesn't just return immediately?

greydamian
  • 153
  • 2
  • 9
  • 5
    Why not try to avoid calling `System.exit()` and instead arrange a proper cleanup and let the JVM exit naturally? – assylias Jul 18 '13 at 15:51
  • 1
    Also note that "*If `System.exit()` is invoked after the virtual machine has begun its shutdown sequence then if shutdown hooks are being run this method will block indefinitely.*" - do you call `System.exit()` twice by any chance? ([Reference](http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#exit%28int%29)) – assylias Jul 18 '13 at 15:53

4 Answers4

2

System.exit if successful does not return even via throwing an exception, so the thread will never complete. This wasn't an issue before shutdown hooks.

Workaround would be to usual standard locks (or even just hack it with new Thread(new Runnable() { public void run() { System.exit(0); }}).start();).

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
1

What you are doing is specifically prohibited:

Shutdown hooks should also finish their work quickly.

Joining random threads cannot be described as 'quick'.

Obviously those threads should close their own streams when they exit. If they don't exit, how will calling join() accomplish anything?

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thanks for your reply. Apologies, to try to keep my question short and to the point, I obviously omitted quite a lot of code. I actually first call a method against the threads to tell them they should finish ASAP, before I call the `Thead.join()`s. Unfortunately, the threads don't have independent resources. So they can't close their own resources in case the other thread is still using them. – greydamian Jul 19 '13 at 07:55
0

It is not recommended to use System.exit() as that just terminates the JVM. There are other concurrency tools like a CountDownLatch. You could even use a finally to clean up correctly on shutdown.

Joshua
  • 26,234
  • 22
  • 77
  • 106
0

Use executors and don't use join or manually create threads.

When using an executor and the other stuff in java.util.concurrent, you can simply call shutdown and wait for it to terminate, specify a timeout, etc. Much more robust. If you are using java 1.7, use try with resources to close your streams as well. That way, you don't have much cleanup work to do beyond shutting down your executor. If not, use a finally block. Never call close anywhere else than from a finally block.

Jilles van Gurp
  • 7,927
  • 4
  • 38
  • 46