How to wait for all tasks in an ThreadPoolExecutor to finish without shutting down the Executor?

JavaAndroidMultithreadingWaitThreadpoolexecutor

Java Problem Overview


I can't use shutdown() and awaitTermination() because it is possible new tasks will be added to the ThreadPoolExecutor while it is waiting.

So I'm looking for a way to wait until the ThreadPoolExecutor has emptied it's queue and finished all of it's tasks without stopping new tasks from being added before that point.

If it makes any difference, this is for Android.

Thanks

Update: Many weeks later after revisiting this, I discovered that a modified CountDownLatch worked better for me in this case. I'll keep the answer marked because it applies more to what I asked.

Java Solutions


Solution 1 - Java

If you are interested in knowing when a certain task completes, or a certain batch of tasks, you may use ExecutorService.submit(Runnable). Invoking this method returns a Future object which may be placed into a Collection which your main thread will then iterate over calling Future.get() for each one. This will cause your main thread to halt execution until the ExecutorService has processed all of the Runnable tasks.

Collection<Future<?>> futures = new LinkedList<Future<?>>();
futures.add(executorService.submit(myRunnable));
for (Future<?> future:futures) {
    future.get();
}

Solution 2 - Java

My Scenario is a web crawler to fetch some information from a web site then processing them. A ThreadPoolExecutor is used to speed up the process because many pages can be loaded in the time. So new tasks will be created in the existing task because the crawler will follow hyperlinks in each page. The problem is the same: the main thread do not know when all the tasks are completed and it can start to process the result. I use a simple way to determine this. It is not very elegant but works in my case:

while (executor.getTaskCount()!=executor.getCompletedTaskCount()){
    System.err.println("count="+executor.getTaskCount()+","+executor.getCompletedTaskCount());
    Thread.sleep(5000);
}
executor.shutdown();
executor.awaitTermination(60, TimeUnit.SECONDS);

Solution 3 - Java

Maybe you are looking for a CompletionService to manage batches of task, see also this answer.

Solution 4 - Java

(This is an attempt to reproduce Thilo's earlier, deleted answer with my own adjustments.)

I think you may need to clarify your question since there is an implicit infinite condition... at some point you have to decide to shut down your executor, and at that point it won't accept any more tasks. Your question seems to imply that you want to wait until you know that no further tasks will be submitted, which you can only know in your own application code.

The following answer will allow you to smoothly transition to a new TPE (for whatever reason), completing all the currently-submitted tasks, and not rejecting new tasks to the new TPE. It might answer your question. @Thilo's might also.

Assuming you have defined somewhere a visible TPE in use as such:

AtomicReference<ThreadPoolExecutor> publiclyAvailableTPE = ...;

You can then write the TPE swap routine as such. It could also be written using a synchronized method, but I think this is simpler:

void rotateTPE()
{
   ThreadPoolExecutor newTPE = createNewTPE();
   // atomic swap with publicly-visible TPE
   ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(newTPE);
   oldTPE.shutdown();
    
   // and if you want this method to block awaiting completion of old tasks in  
   // the previously visible TPE
   oldTPE.awaitTermination();
} 

Alternatively, if you really no kidding want to kill the thread pool, then your submitter side will need to cope with rejected tasks at some point, and you could use null for the new TPE:

void killTPE()
{
   ThreadPoolExecutor oldTPE = publiclyAvailableTPE.getAndSet(null);
   oldTPE.shutdown();
    
   // and if you want this method to block awaiting completion of old tasks in  
   // the previously visible TPE
   oldTPE.awaitTermination();
} 

Which could cause upstream problems, the caller would need to know what to do with a null.

You could also swap out with a dummy TPE that simply rejected every new execution, but that's equivalent to what happens if you call shutdown() on the TPE.

Solution 5 - Java

If you don't want to use shutdown, follow below approaches:

  1. Iterate through all Future tasks from submit on ExecutorService and check the status with blocking call get() on Future object as suggested by Tim Bender

  2. Use one of

    1. Using invokeAll on ExecutorService
    2. Using CountDownLatch
    3. Using ForkJoinPool or newWorkStealingPool of Executors(since java 8)

invokeAll() on executor service also achieves the same purpose of CountDownLatch

Related SE question:

https://stackoverflow.com/questions/1252190/how-to-wait-for-a-set-of-threads-to-complete/37116094#37116094

Solution 6 - Java

You could call the waitTillDone() on Runner class:

Runner runner = Runner.runner(10);

runner.runIn(2, SECONDS, runnable);
runner.run(runnable); // each of this runnables could submit more tasks

runner.waitTillDone(); // blocks until all tasks are finished (or failed)

// and now reuse it

runner.runIn(500, MILLISECONDS, callable);

runner.waitTillDone();
runner.shutdown();

To use it add this gradle/maven dependency to your project: 'com.github.matejtymes:javafixes:1.0'

For more details look here: https://github.com/MatejTymes/JavaFixes or here: http://matejtymes.blogspot.com/2016/04/executor-that-notifies-you-when-task.html

Solution 7 - Java

Try using queue size and active tasks count as shown below

 while (executor.getThreadPoolExecutor().getActiveCount() != 0 || !executor.getThreadPoolExecutor().getQueue().isEmpty()){
                     try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioncottonBallPawsView Question on Stackoverflow
Solution 1 - JavaTim BenderView Answer on Stackoverflow
Solution 2 - Javagoogol4uView Answer on Stackoverflow
Solution 3 - JavaThiloView Answer on Stackoverflow
Solution 4 - JavaandersojView Answer on Stackoverflow
Solution 5 - JavaRavindra babuView Answer on Stackoverflow
Solution 6 - JavaMatej TymesView Answer on Stackoverflow
Solution 7 - JavaTuğrul KarakayaView Answer on Stackoverflow