ExecutorService, how to wait for all tasks to finish

前端 未结 15 2106
攒了一身酷
攒了一身酷 2020-11-22 15:45

What is the simplest way to to wait for all tasks of ExecutorService to finish? My task is primarily computational, so I just want to run a large number of jobs

相关标签:
15条回答
  • 2020-11-22 16:33

    Add all threads in collection and submit it using invokeAll. If you can use invokeAll method of ExecutorService, JVM won’t proceed to next line until all threads are complete.

    Here there is a good example: invokeAll via ExecutorService

    0 讨论(0)
  • 2020-11-22 16:35

    Sounds like you need ForkJoinPool and use the global pool to execute tasks.

    public static void main(String[] args) {
        // the default `commonPool` should be sufficient for many cases.
        ForkJoinPool pool = ForkJoinPool.commonPool(); 
        // The root of your task that may spawn other tasks. 
        // Make sure it submits the additional tasks to the same executor that it is in.
        Runnable rootTask = new YourTask(pool); 
        pool.execute(rootTask);
        pool.awaitQuiescence(...);
        // that's it.
    }
    

    The beauty is in pool.awaitQuiescence where the method will block utilize the caller's thread to execute its tasks and then return when it is really empty.

    0 讨论(0)
  • 2020-11-22 16:43

    You can use ExecutorService.invokeAll method, It will execute all task and wait till all threads finished their task.

    Here is complete javadoc

    You can also user overloaded version of this method to specify the timeout.

    Here is sample code with ExecutorService.invokeAll

    public class Test {
        public static void main(String[] args) throws InterruptedException, ExecutionException {
            ExecutorService service = Executors.newFixedThreadPool(3);
            List<Callable<String>> taskList = new ArrayList<>();
            taskList.add(new Task1());
            taskList.add(new Task2());
            List<Future<String>> results = service.invokeAll(taskList);
            for (Future<String> f : results) {
                System.out.println(f.get());
            }
        }
    
    }
    
    class Task1 implements Callable<String> {
        @Override
        public String call() throws Exception {
            try {
                Thread.sleep(2000);
                return "Task 1 done";
            } catch (Exception e) {
                e.printStackTrace();
                return " error in task1";
            }
        }
    }
    
    class Task2 implements Callable<String> {
        @Override
        public String call() throws Exception {
            try {
                Thread.sleep(3000);
                return "Task 2 done";
            } catch (Exception e) {
                e.printStackTrace();
                return " error in task2";
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-22 16:43

    how about this?

    Object lock = new Object();
    CountDownLatch cdl = new CountDownLatch(threadNum);
    for (int i = 0; i < threadNum; i++) {
        executorService.execute(() -> {
    
            synchronized (lock) {
                cdl.countDown();
                try {
                    lock.wait();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        });
    }
    cdl.await();
    synchronized (lock) {
        lock.notifyAll();
    }
    

    if you do not add new tasks to ExecutorService , this may wait for all current tasks completed

    0 讨论(0)
  • 2020-11-22 16:45

    I will just wait for the executor to terminate with a specified timeout that you think it is suitable for the tasks to complete.

     try {  
             //do stuff here 
             exe.execute(thread);
        } finally {
            exe.shutdown();
        }
        boolean result = exe.awaitTermination(4, TimeUnit.HOURS);
        if (!result)
    
        {
            LOGGER.error("It took more than 4 hour for the executor to stop, this shouldn't be the normal behaviour.");
        }
    
    0 讨论(0)
  • 2020-11-22 16:48

    The simplest approach is to use ExecutorService.invokeAll() which does what you want in a one-liner. In your parlance, you'll need to modify or wrap ComputeDTask to implement Callable<>, which can give you quite a bit more flexibility. Probably in your app there is a meaningful implementation of Callable.call(), but here's a way to wrap it if not using Executors.callable().

    ExecutorService es = Executors.newFixedThreadPool(2);
    List<Callable<Object>> todo = new ArrayList<Callable<Object>>(singleTable.size());
    
    for (DataTable singleTable: uniquePhrases) { 
        todo.add(Executors.callable(new ComputeDTask(singleTable))); 
    }
    
    List<Future<Object>> answers = es.invokeAll(todo);
    

    As others have pointed out, you could use the timeout version of invokeAll() if appropriate. In this example, answers is going to contain a bunch of Futures which will return nulls (see definition of Executors.callable(). Probably what you want to do is a slight refactoring so you can get a useful answer back, or a reference to the underlying ComputeDTask, but I can't tell from your example.

    If it isn't clear, note that invokeAll() will not return until all the tasks are completed. (i.e., all the Futures in your answers collection will report .isDone() if asked.) This avoids all the manual shutdown, awaitTermination, etc... and allows you to reuse this ExecutorService neatly for multiple cycles, if desired.

    There are a few related questions on SO:

    • How to wait for all threads to finish

    • Return values from java threads

    • invokeAll() not willing to accept a Collection<Callable<t>>

    • Do I need to synchronize?

    None of these are strictly on-point for your question, but they do provide a bit of color about how folks think Executor/ExecutorService ought to be used.

    0 讨论(0)
提交回复
热议问题