How to Shutdown a Java ExecutorService

ExecutorService interface provides 3 methods shutdown(), shutdownNow() and awaitTermination​() for controlling the termination of tasks submitted to executors. Learn how to use these methods under different requirements.

1. Difference between shutdown(), shutdownNow() and awaitTermination​()

Let us start with checking out the syntax of these methods.

void shutdown();
 
List<Runnable> shutdownNow();
 
boolean awaitTermination(long timeout, TimeUnit unit);

The shutdown() initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. This method does not wait for previously submitted tasks (but not started executing) to complete execution.

The shutdownNow(), forcibly, attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution. This method does not wait for actively executing tasks to terminate and tries to stop them forcefully. There are no guarantees beyond best-effort attempts to stop processing actively executing tasks. This implementation cancels tasks via Thread.interrupt(), so any task that fails to respond to interrupts may never terminate.

The awaitTermination(long timeout, TimeUnit unit) blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.

Remember that awaitTermination() is invoked after a shutdown() request.

2. Using shutdown() and awaitTermination​()

2.1 When to Use

In general, the ExecutorService will not be automatically destroyed when there is no task to process. It will stay alive and wait for new tasks to come. It simply means that JVM will not terminate if we are expecting it to.

This behavior is helpful in the case of web/desktop applications that perform these tasks for the infinite duration on basis of when new tasks arrive.

But when we have an application that does not use the executor very frequently – and we want to reclaim the memory for other components to use – we must shutdown the executor. But we cannot shutdown the executor just like that. We must be sure that the current set of tasks submitted to the executor must be completed and shall not get killed in the process.

To allow the previously submitted tasks to complete; and do not allow new tasks to executor; and wait for submitted tasks to complete – we have to use shutdown() with awaitTermination​().

2.2. Example

In following example, we have scheduled 3 tasks which will be executed after 10, 20 and 30 seconds delay.

After 15 seconds of scheduling the tasks, we shut down the executor but do not block the program execution as print statements get executed. At this moment, only one task has been completed that executed after 10 seconds delay.

Once we call awaitTermination(), it blocks the execution and wait until all tasks are finished. Once all tasks are executed, program execution resumes, and now we can access the results of all tasks completed by the executor service.

public class ShutdownExecutor {
  public static void main(String[] args) throws InterruptedException {
    ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);

    System.out.println("WorkerTasks scheduled at : " + LocalDateTime.now());

    ScheduledFuture<String> result1 = executor.schedule(new WorkerTask(
            "WorkerTask-1"), 10, TimeUnit.SECONDS);

    ScheduledFuture<String> result2 = executor.schedule(new WorkerTask(
            "WorkerTask-2"), 20, TimeUnit.SECONDS);

    ScheduledFuture<String> result3 = executor.schedule(new WorkerTask(
            "WorkerTask-3"), 30, TimeUnit.SECONDS);

    Thread.sleep(15_000);
    System.out.println("***********Shutting down the executor service*********");
    executor.shutdown();

    System.out.println("***********Tasks are partially completed*********");

    System.out.println("Task-1 is done : " + result1.isDone());
    System.out.println("Task-2 is done : " + result2.isDone());
    System.out.println("Task-3 is done : " + result3.isDone());

    System.out.println("***********Waiting for tasks to be complete*********");
    executor.awaitTermination(1, TimeUnit.MINUTES);

    System.out.println("***********All tasks are completed now*********");

    System.out.println("Task-1 is done : " + result1.isDone());
    System.out.println("Task-2 is done : " + result2.isDone());
    System.out.println("Task-3 is done : " + result3.isDone());
  }
}

class WorkerTask implements Callable<String> {
  private final String name;

  public WorkerTask(String name) {
    this.name = name;
  }

  @Override
  public String call() throws Exception {
    System.out.println("WorkerTask [" + name + "] executed on : "
        + LocalDateTime.now().toString());
    return "WorkerTask [" + name + "] is SUCCESS !!";
  }
}

Program output.

WorkerTasks scheduled at : 2022-08-07T01:56:21.446915400
WorkerTask [WorkerTask-1] executed on : 2022-08-07T01:56:31.466320300
***********Shutting down the executor service*********
***********Tasks are partially completed*********
Task-1 is done : true
Task-2 is done : false
Task-3 is done : false
***********Waiting for tasks to be complete*********
WorkerTask [WorkerTask-2] executed on : 2022-08-07T01:56:41.466846400
WorkerTask [WorkerTask-3] executed on : 2022-08-07T01:56:51.470540700
***********All tasks are completed now*********
Task-1 is done : true
Task-2 is done : true
Task-3 is done : true

3. Using shutdownNow()

The shutdownNow() is a hard signal to destroy ExecutorService immediately along with stopping the execution of all in-progress and queued tasks. Use this method, when we want the application to stop processing all tasks immediately.

If in the previous example, we replace with executor.shutdown() to executor.shutdownNow(), we will see that as soon as shutdownNow() is invoked, program execution does not block anywhere, and task executions are stopped immediately. Here awaitTermination() does not have any effect in this case.

Thread.sleep(15_000);

System.out.println("***********Shutting down the executor service*********");
executor.shutdownNow();

Program output.

WorkerTasks scheduled at : 2022-08-07T01:57:25.427667900
WorkerTask [WorkerTask-1] executed on : 2022-08-07T01:57:35.436689400
***********Shutting down the executor service*********
***********Tasks are partially completed*********
Task-1 is done : true
Task-2 is done : false
Task-3 is done : false
***********Waiting for tasks to be complete*********
***********All tasks are completed now*********
Task-1 is done : true
Task-2 is done : false
Task-3 is done : false

4. Best Practice

The correct way to shutdown the executor service, as suggested in Java docs, is as follows. It shuts down the executor service and waits for some time for submitted tasks to complete.

If the running tasks do not complete in certain time, they are terminated forcefully.

void shutdownAndAwaitTermination(ExecutorService pool) {
    // Disable new tasks from being submitted
    pool.shutdown();
    try {
        // Wait a while for existing tasks to terminate
        if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
            // Cancel currently executing tasks forcefully
            pool.shutdownNow();
            // Wait a while for tasks to respond to being cancelled
            if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                System.err.println("Pool did not terminate");
        }
    } catch (InterruptedException ex) {
        // (Re-)Cancel if current thread also interrupted
        pool.shutdownNow();
        // Preserve interrupt status
        Thread.currentThread().interrupt();
    }
}

To use this method, simply pass the ExecutorService reference to this method.

ExecutorService executorService = Executors.newFixedThreadPool(1);

executorService.submit(new WorkerTask("1"));
executorService.submit(new WorkerTask("2"));
executorService.submit(new WorkerTask("3"));

shutdownAndAwaitTermination(executorService);

Happy Learning !!

Source Code on Github

Leave a Reply

1 Comment
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions, and frequently asked interview questions.

Our Blogs

REST API Tutorial