Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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!");

My question:

  • Why submit() and shutdown() don't wait until all threads will be finished and prints «We're done! All threads are finished!» right after call of service.shutdown();?
like image 461
Mike Avatar asked Apr 02 '16 16:04

Mike


People also ask

How do you wait for threads to finish?

The wait() and join() methods are used to pause the current thread. The wait() is used in with notify() and notifyAll() methods, but join() is used in Java to wait until one thread finishes its execution.

Is it necessary to shutdown ExecutorService?

Upon termination, an executor has no tasks actively executing, no tasks awaiting execution, and no new tasks can be submitted. An unused ExecutorService should be shut down to allow reclamation of its resources. Method submit extends base method Executor. execute(java.

How do I stop the ExecutorService thread?

To properly shut down an ExecutorService, we have the shutdown() and shutdownNow() APIs. This method returns a list of tasks that are waiting to be processed. It is up to the developer to decide what to do with these tasks.


3 Answers

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
like image 88
Adam Siemion Avatar answered Oct 08 '22 03:10

Adam Siemion


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);
 }  
like image 25
Ravindra babu Avatar answered Oct 08 '22 02:10

Ravindra babu


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();
}
like image 2
Mike Avatar answered Oct 08 '22 02:10

Mike