17 How to Properly Close Thread Pool Shutdown vs Shutdown Now Differences

17 How to Properly Close ThreadPool shutdown vs shutdownNow Differences #

In this lesson, we will mainly learn how to properly shut down a thread pool and the differences between the shutdown() and shutdownNow() methods. First, let’s create a thread pool with a fixed size of 10 threads and submit 100 tasks to the thread pool, as shown in the code.

ExecutorService service = Executors.newFixedThreadPool(10);

for (int i = 0; i < 100; i++) {
    service.execute(new Task());
}

So how can we shut down this thread pool now? This lesson introduces 5 methods related to shutting down a thread pool in ThreadPoolExecutor.

  • void shutdown;
  • boolean isShutdown;
  • boolean isTerminated;
  • boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;
  • List shutdownNow;

Let’s elaborate on these methods one by one.

shutdown() #

The first method is called shutdown(). It safely shuts down a thread pool. After calling the shutdown() method, the thread pool is not immediately closed because there may still be many tasks being executed in the thread pool or a large number of tasks waiting to be executed in the task queue. After calling the shutdown() method, the thread pool will completely close only after executing the tasks that are currently being executed and the tasks waiting in the queue. However, this does not mean that the shutdown() operation has no effect. After calling the shutdown() method, if new tasks are submitted, the thread pool will reject the subsequent new tasks based on the rejection policy.

isShutdown() #

The second method is called isShutdown(). It returns true or false to determine whether the thread pool has started the shutdown process, that is, whether the shutdown or shutdownNow method has been executed. It is important to note that if the result of calling the isShutdown() method is true, it does not mean that the thread pool has been completely closed at this time. It only means that the thread pool has started the process of closing. This means that there may still be threads executing tasks and tasks waiting in the queue.

isTerminated() #

The third method is called isTerminated(). This method can be used to check whether the thread pool has truly terminated. It indicates that not only has the thread pool been closed, but also all tasks in the thread pool have been completed. As mentioned earlier, after calling the shutdown method, the thread pool will continue to execute the unfinished tasks, including not only the tasks being executed by threads, but also the tasks waiting in the task queue. For example, if the shutdown method has been called, but one thread is still executing a task, then calling the isShutdown method will return true, while calling the isTerminated method will return false because there are still tasks being executed in the thread pool. Only when all tasks have been completed, the isTerminated() method will return true, indicating that the thread pool has been closed and the thread pool is empty, and all remaining tasks have been completed.

awaitTermination() #

The fourth method is called awaitTermination(). It is not used to shut down the thread pool itself, but mainly used to determine the status of the thread pool. For example, if we pass in a timeout value of 10 seconds to the awaitTermination method, it will wait for 10 seconds. It will wait until one of the following three situations occurs:

  1. During the wait (including before entering the wait state), the thread pool has been closed and all submitted tasks (including those being executed and those waiting in the queue) have been completed. This means that the thread pool has truly terminated, and the method will return true.
  2. After the wait timeout, the first situation of the thread pool termination has not occurred, so the method will return false.
  3. During the wait, the thread is interrupted, and the method will throw an InterruptedException.

In other words, after calling the awaitTermination method, the current thread will try to wait for a specified period of time. If within the waiting time, the thread pool has been closed and all internal tasks have been completed, that is, the thread pool has truly terminated, then the method will return true; otherwise, it will return false if the timeout is exceeded.

Based on the boolean value returned by awaitTermination(), we can determine the next operation to be executed.

shutdownNow() #

The last method is shutdownNow(), which is the most powerful method among the 5 methods. The difference from the first shutdown method is the additional word “Now” in the name, which means immediate closure. After executing the shutdownNow method, it will first send an interrupt signal to all threads in the thread pool, trying to interrupt the execution of these tasks. Then it will move all the tasks waiting in the task queue to a List and return it. Based on the returned task List, we can perform some remedial operations, such as recording for future retries. The source code of shutdownNow() is as follows.

public List<Runnable> shutdownNow() {

    List<Runnable> tasks;

    final ReentrantLock mainLock = this.mainLock;

    mainLock.lock();

    try {

        checkShutdownAccess();

        advanceRunState(STOP);

        interruptWorkers();

        tasks = drainQueue();

    } finally {

        mainLock.unlock();

    }

    tryTerminate();

    return tasks;

}

You can see that there is a line of code interruptWorkers() in the source code. This line of code will interrupt each launched thread, so that the thread can detect the interrupt signal during task execution and perform corresponding processing to terminate the task in advance. It is important to note that, due to the limitation of not recommending the mechanism of forcibly stopping threads in Java, even if we call the shutdownNow method, if the interrupted thread ignores the interrupt signal, it may still cause the task not to stop. This indicates that it is important to implement the best practices in development. The threads we write should have the ability to respond to interrupt signals, and the correct method to stop threads was discussed in Lesson 2, which is to use interrupt signal to coordinate work.

After mastering these 5 methods related to shutting down a thread pool, we can choose the appropriate method to stop the thread pool based on our business needs. For example, we usually use the shutdown() method to close the thread pool, which allows the submitted tasks to be completed. However, if the situation is urgent, we can use the shutdownNow method to expedite the termination of the thread pool.