Reputation: 988
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?
Upvotes: 1
Views: 655
Reputation: 339837
The Answer by MarcoLucidi is correct and should be accepted. Here are a few more thoughts.
You said:
I was expecting the code to await the completion of the previous threads when running "threadPool.shutdown()",
ExecutorService threadPool
These indicate that you think of the executor service as a thread pool. You should not. The Executors framework was invented to relieve you of the chore of managing threads.
Regarding ExecutorService
… The point of the word “service” is that its job gets done without you knowing or caring about the details. And the other word “executor” refers to its job being the execution of your Runnable
/Callable
tasks. No mention of “thread” is deliberate.
So after establishing your ExecutorService
object, you should no longer be thinking in terms of thread pools. The only concern for threads would be related to your tasks accessing resources across threads.
This point will be even more relevant in the future with the arrival of virtual threads being developed as part of Project Loom.
Work is being done in Project Loom to greatly enhance the concurrency facilities in Java. Experimental builds are available now, based on early-access Java 18.
AutoCloseable
One convenient enhancement is making ExecutorService
AutoCloseable
. This means we can use try-with-resources syntax.
When all your assigned tasks are complete, the automatic closing handles the shutting down of the executor service and its backing thread pool. No need for you to call the shutdown
& awaitTermination
methods.
Your code will be simpler and more obvious.
try (
ExecutorService executorService = Executors.… ;
) {
// Submit your `Runnable`/`Callable` tasks to the executor service.
…
}
// At this point, flow-of-control blocks until all submitted tasks are done/canceled/failed.
// After this point, the executor service will have been automatically shutdown, wia `close` method called by try-with-resources syntax.
Upvotes: 0
Reputation: 2177
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:
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()
.
Upvotes: 3