简体   繁体   中英

Wait for all tasks in ExecutorService.execute to complete

How to wait for all tasks to be completed when they are submitted using
ExecutorService.execute() . There is a function called awaitTermination
But a timeout has to be provided in it. Which is not a guarantee that when this
returns all the tasks would have been finished. Is there a way to achieve this?

If you read the javadoc of the ExecutorService.awaitTermination (or look at the method signature) you will see it returns a boolean . This boolean indicates if the Executor terminated or not. You can use that information to create a while loop to determine if it has been terminated or not.

ExecutorService executor = ...

executor.shutdown(); // close the executor and don't accept new tasks

while (!executor.awaitTermination(100, TimeUnit.MILLISECONDS) {}

Something like this will stop the executor and wait until it terminated and all tasks have finished.

execute method does not return anything. You can use the submit method which returns a result of type Future.

Future<String> future = 
  executorService.submit(callableTask/runnableTask);

If you use class ThreadPoolExecutor or any of its children you have a method there getActiveCount() that returns the number of threads that are actively executing tasks. So you can poll that method until it gets to 0, which would mean that all tasks have been completed and no new tasks are currently executing. However, what if some task gets stuck? I think you will have to also give some timeout in order to prevent infinite loop in this case.

The biggest advantage of this idea is that you are not required to invoke shutdown method

There are several approaches.

You can call first ExecutorService.shutdown and then ExecutorService.awaitTermination which returns:

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

So:

There is a function called awaitTermination But a timeout has to be provided in it. Which is not a guarantee that when this returns all the tasks would have been finished. Is there a way to achieve this?

You just have to call awaitTermination in a loop.

Using awaitTermination

A full example with this implementation:

public class WaitForAllToEnd {

    public static void main(String[] args) throws InterruptedException {
        final int total_threads = 4;
        ExecutorService executor = Executors.newFixedThreadPool(total_threads);
        for(int i = 0; i < total_threads; i++){
            executor.execute(parallelWork(100 + i * 100));
        }

        int count = 0;

        // This is the relevant part
        // Chose the delay most appropriate for your use case
        executor.shutdown();
        while (!executor.awaitTermination(100, TimeUnit.MILLISECONDS)) {
            System.out.println("Waiting "+ count);
            count++;
        }
    }

    private static Runnable parallelWork(long sleepMillis) {
        return () -> {
            try {
                Thread.sleep(sleepMillis);
            } catch (InterruptedException e) {
                // Do Something
            }
            System.out.println("I am Thread : " + Thread.currentThread().getId());
        };
    }
}

Using CountDownLatch

Another option is to create a CountDownLatch with a count equals to the number of parallel tasks. Each thread calls countDownLatch.countDown(); , while the main thread calls countDownLatch.await(); .

A full example with this implementation:

public class WaitForAllToEnd {

    public static void main(String[] args) throws InterruptedException {
        final int total_threads = 4;
        CountDownLatch countDownLatch = new CountDownLatch(total_threads);
        ExecutorService executor = Executors.newFixedThreadPool(total_threads);
        for(int i = 0; i < total_threads; i++){
            executor.execute(parallelWork(100 + i * 100, countDownLatch));
        }
        countDownLatch.await();
        System.out.println("Exit");
        executor.shutdown();
    }

    private static Runnable parallelWork(long sleepMillis, CountDownLatch countDownLatch) {
        return () -> {
            try {
                Thread.sleep(sleepMillis);
            } catch (InterruptedException e) {
                // Do Something
            }
            System.out.println("I am Thread : " + Thread.currentThread().getId());
            countDownLatch.countDown();
        };
    }
}

Using Cyclic Barrier

Another approach is to use a Cyclic Barrier

public class WaitForAllToEnd {

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        final int total_threads = 4;
        CyclicBarrier barrier = new CyclicBarrier(total_threads+ 1);
        ExecutorService executor = Executors.newFixedThreadPool(total_threads);
        for(int i = 0; i < total_threads; i++){
            executor.execute(parallelWork(100 + i * 100, barrier));
        }
        barrier.await();
        System.out.println("Exit");
        executor.shutdown();
    }

    private static Runnable parallelWork(long sleepMillis, CyclicBarrier barrier) {
        return () -> {
            try {
                Thread.sleep(sleepMillis);
            } catch (InterruptedException e) {
                // Do Something
            }
            System.out.println("I am Thread : " + Thread.currentThread().getId());
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
              // Do something
            }
        };
    }
}

There are other approaches but those would require changes to your initial requirements, namely:

How to wait for all tasks to be completed when they are submitted using ExecutorService.execute().

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM