Wednesday, October 17, 2018

How to exit java ExecutorService without calling shutdown method

The java Executor framework encapsulated the thread lifecycle management under interface ExecutorService. By default, the ExecutorService implementation returned by Executors needs programmer to call shutdown() method to stop it. Forget to call shutdown for an ExecutorService will prevent the JVM to exit. The JVM won't exit if there are any non-daemon thread still running. The worker threads in the ExecutorService's thread pool won't exit unless you interrupt them, in another words, they will still be running after the main thread exits. These worker threads prevent JVM to exit.

For example, the following program will never finish.


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TheadTester {
public static void main(String... args) {
TheadTester ts = new TheadTester();
ts.test();
}

private void test() {
ExecutorService exec = Executors.newSingleThreadExecutor();
exec.execute(() -> System.out.println("ok"));
//exec.shutdown();
}
}

You can override the default ThreadFactory, so that the worker thread in the thread pool will be daemon thread. Since daemon thread won't prevent JVM to exit, you don't have to call ExecutorService's shutdown() method. This approach has drawbacks, though. Once the main thread exits, the only threads remain are the worker daemon threads in the threadpool. When JVM exits, these threads may still have work to do. Since they are daemons, JVM won't wait them to finish. You need to give enough time for these daemon threads to finish the work and run to the last line so that they can exit normally instead of abruptly.


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TheadTester {
public static void main(String... args) throws InterruptedException {
TheadTester ts = new TheadTester();
ts.test();
//give enough time for deamon thread to finish
Thread.sleep(2000);
}

private void test() {
ExecutorService exec = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
exec.execute(() -> System.out.println("ok"));
//exec.shutdown();
}
}

This trick may be trivial, but it could be handy in big java project. For example, you need to add an ad-hoc threadpool in a class in a big project. If you use default ThreadFactory for the ExecutorService, you have to shutdown the ExecutorService somewhere. This "somewhere" might be in a thread that shutdown project level services before main thread exits, which you would rather not touch. You just want to create an ad-hoc threapool to handle events for a shot period of time, finish it, then exit. Next time, when new events come, you create a new threadpool to handle it. So on and so forth. The deamon worker strategy might be a good choice in this situation.

1 comment:

Why I stopped publishing blog posts as information provider

Now the AI can generate content. Does that mean the web publishing industry reaches the end? ChatGPT said: ChatGPT Not at all. While AI can ...