ExecutorService's shutdown() doesn't wait until all threads will be finished
Asked Answered
D

3

8

I have a code where 4 threads run at the same time. I want to wait until all these 4 threads will be finished. And only after that to continue the app flow.

I tried two approaches:

  1. Thread#join(), this approach works as expected. The code, which comes after join() is executed only after all threads are finished.
  2. ExecutorService#shutdown(), this technique allows executing code, which comes after shutdown() even if not all threads are finished.

Code sample:

ExecutorService service = Executors.newFixedThreadPool(cpuCoresNum);

for (int i = 0; i < cpuCoresNum; i++) {

    service.submit(() -> {
        try {
            foo(); // some long execution function
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

service.shutdown();

System.out.println("We're done! All threads are finished!");

Why don't submit() and shutdown() wait until all threads will be finished and prints «We're done! All threads are finished!» right after call of service.shutdown();?

Dolt answered 2/4, 2016 at 16:58 Comment(0)
O
11

The answer is available in the ExecutorService.shutdown() Javadoc:

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

If you want to wait for the threads to finish work you have the following options:

  • get Future instances returned by submit() and call get() on every Future instance
  • after calling shutdown on service call awaitTermination on service until it returns true
  • instead of calling submit on service add your Runnable instances to a java.util.List and pass this list to the invokeAll method called on service
Obturate answered 2/4, 2016 at 17:7 Comment(7)
My threads don't return any value, thus, the second option seems to be more relevant.Dolt
@MikeB. submit returns a Future regardless whether you pass a Runnable or Callable, in both cases calling get() on a Future waits for the thread to complete its work. Before calling awaitTermination you have to call shutdown.Obturate
awaitTermination() requires some timeout to specify but in my case I don't know how much time it should take, thus, what value I have to specify as a timeout in order to assure that al threads will be finished correctly and not forced to stop due to timeout? From documentation: «Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens firstDolt
Another option I would add for waiting until all of the executions are finished would be to give each thread a reference to a CountDownLatch which is initialized with the number of threads. Call latch.await() in your main thread and have the worker threads call latch.countDown() when they're finished.Purposive
@MikeB. You can pass a very large number as a timeout e.g. Long.MAX_VALUE however it is always good to provide a timeout as your code can always block/deadlock that is why the JDK designers made this attribute mandatory, I guess.Obturate
Waiting on a single Future does not guaranty that all jobs are finished. So the first bullet is not a real option.Healthy
@Healthy of course when you call get on a single Future it only guarantees this Future is completed. I have updated the answer to avoid ambiguity.Obturate
A
3

Recommended way from oracle documentation page of ExecutorService:

 void shutdownAndAwaitTermination(ExecutorService pool) {
   pool.shutdown(); // Disable new tasks from being submitted
   try {
     // Wait a while for existing tasks to terminate
     if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
       pool.shutdownNow(); // Cancel currently executing tasks
       // 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 ie) {
     // (Re-)Cancel if current thread also interrupted
     pool.shutdownNow();
     // Preserve interrupt status
     Thread.currentThread().interrupt();
   }

shutdown(): Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.

shutdownNow():Attempts to stop all actively executing tasks, halts the processing of waiting tasks, and returns a list of the tasks that were awaiting execution.

In above example, if your tasks are taking more time to complete, you can change if condition to while condition

Replace

if (!pool.awaitTermination(60, TimeUnit.SECONDS))

with

 while(!pool.awaitTermination(60, TimeUnit.SECONDS)) {
     Thread.sleep(60000);
 }  
Amaras answered 5/4, 2016 at 18:31 Comment(0)
D
2

Thanks to @Adam Siemion suggestions, here is a final code:

ExecutorService service = Executors.newFixedThreadPool(cpuCoresNum);

int itNum = 1;

for (int i = 0; i < cpuCoresNum; i++) {

    int treadID = itNum++;

    service.submit(() -> {
        Thread.currentThread().setName("Thread_#" + treadID);
        try {
            foo();
        } catch (Exception e) {
            e.printStackTrace();
        }
    });
}

// wait until all threads will be finished
service.shutdown();
try {
    service.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
    e.printStackTrace();
}
Dolt answered 2/4, 2016 at 20:2 Comment(1)
Thanks for sharing the solutionPerplex

© 2022 - 2024 — McMap. All rights reserved.