Shutting Down a Thread Executor

One using a thread executor is finished, it is important to call the shutdown() method on an instance of an ExecutorService interface. A thread executor creates a non-daemon thread on the first task that is executed, so failing to call shutdown() will result in the application never terminating.

The shutdown process for a thread executor involves first rejecting any new tasks submitted to the thread executor while continuing to execute any previously submitted tasks. During this time, calling isShutdown() will return true, while isTerminated will return false. If a new task is submitted to the thread executor while it is shutting down, a RejectedExecutionException will be thrown. Once all active tasks have been completed, isShutdown() and isTerminated() will both return true. The table below summarizes the shutdown process :

Active Shutting Down Shutdown
Accepts New Tasks Rejects New Tasks Rejects New Tasks
Executes Tasks Executes Tasks No Tasks Running
isShutdown() = false isShutdown() = true isShutdown() = true
isTerminated() = false isTerminated() = false isTerminated() = true

shutdown() does not actually stop any tasks that have already been submitted to the thread executor. The ExecutorService provides another method called shutdownNow(), which attempts to stop all running tasks and discards any that have not been started yet. shutdownNow() attempts to stop all running tasks. Besides, shutdownNow() returns a List<Runnable> of tasks that were submitted to the thread executor but were never started.

Since the ExecutorService interface does not implement AutoCloseable interface, its implementations cannot be used inside try-with-resources statement. So it is considered a good practice to use shutdown in finally block.

ExecutorService service = null;
try {
    service = Executors.newSingleThreadExecutor();
    // Add tasks to thread executor
    ...
} finally {
    if (service != null) {
        service.shutdown();
    }
}

This solution works for thread executors that are used once and thrown away. It does not work for thread executors that are persistent throughout the life of the application. For example, developer can create a static instance of a thread executor and have all processes share it. In such scenario, there must be a static method that can be called anytime the user signals that they wish to exit the program. Or if it is a singleton Spring Bean, @PreDestroy method can serve the same purpose – shutting down ExecutorService implementation. Important : failure to shut down a thread executor after at least one thread has been created will result in the program hanging.

Leave a Reply

Your email address will not be published. Required fields are marked *