Await completion of all ExecutorService Threads

I’m trying to get a grasp on concurrency in Java, I’ve made this simple code that prints the letters of the alphabet :

public static void main(String[] args) throws IOException, InterruptedException, ExecutionException {
    
    final ExecutorService threadPool = Executors.newFixedThreadPool(4);
    final ExecutorCompletionService<Character> completionService = new ExecutorCompletionService<>(threadPool);

    final List<Character> letters = IntStream.range(65, 91).mapToObj(i -> (char) i).collect(Collectors.toList());

    for (char letter : letters) {
      completionService.submit(() -> printLetter(letter));
    }
    
    System.out.println("Starting shutdown");

    threadPool.shutdown(); // I WAS EXPECTING CODE TO STOP HERE, WAITING FOR ALL THREADS COMPLETION
    
    System.out.println("Ending shutdown");

  }

private static char printLetter(char letter) {
    try {
        TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    System.out.println("Hello from: " + letter);
    return letter;
}

When I executed the code above, I was expecting the code to await the completion of the previous threads when running “threadPool.shutdown()“, however it keeps executing the rest of the code as I can see in the output:

Starting shutdown
Ending shutdown
Hello from: B
Hello from: D
....
Hello from: Z

While the desired output for me would be:

Starting shutdown
Hello from: B
Hello from: D
....
Hello from: Z
Ending shutdown

I tried using threadPool.awaitTermination(30, TimeUnit.SECONDS) instead but for a reason I ignore yet, it awaits the full completion of the 30 seconds before continuing even if all letters have been printed.

How can I await the completion of all threads?

Answer

When I executed the code above, I was expecting the code to await the completion of the previous threads when running “threadPool.shutdown()”

this won’t happen, from ExecutorService‘s docs:

void shutdown()

Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted. Invocation has no additional effect if already shut down.

This method does not wait for previously submitted tasks to complete execution. Use awaitTermination to do that.

in order to achieve your result, you need to use both, shutdown() first and then awaitTermination​():

boolean awaitTermination​(long timeout, TimeUnit unit) throws InterruptedException

Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.

returns: true if this executor terminated and false if the timeout elapsed before termination

this behaviour:

I tried using threadPool.awaitTermination(30, TimeUnit.SECONDS) instead but for a reason I ignore yet, it awaits the full completion of the 30 seconds before continuing even if all letters have been printed.

was probably caused by replacing shutdown() with awaitTermination​(). it was waiting on a “non-shutdown” pool. as already said, you need to call shutdown() before awaitTermination​().