How to wait for all threads to finish, using ExecutorService?
我需要一次执行4个任务,如下所示:
1 2 3 4 5 | ExecutorService taskExecutor = Executors.newFixedThreadPool(4); while(...) { taskExecutor.execute(new MyTask()); } //...wait for completion somehow |
一旦完成所有内容,我该如何收到通知? 现在我想不出比设置一些全局任务计数器更好的事情,并在每个任务结束时减少它,然后在无限循环中监视这个计数器变为0; 或获得一个Futures列表,并在无限循环监视器isDone中为所有这些。 什么是更好的解决方案不涉及无限循环?
谢谢。
基本上在
1 2 3 4 5 6 7 8 9 10 | ExecutorService taskExecutor = Executors.newFixedThreadPool(4); while(...) { taskExecutor.execute(new MyTask()); } taskExecutor.shutdown(); try { taskExecutor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS); } catch (InterruptedException e) { ... } |
使用CountDownLatch:
1 2 3 4 5 6 7 8 9 10 11 | CountDownLatch latch = new CountDownLatch(totalNumberOfTasks); ExecutorService taskExecutor = Executors.newFixedThreadPool(4); while(...) { taskExecutor.execute(new MyTask()); } try { latch.await(); } catch (InterruptedException E) { // handle } |
并在你的任务中(包含在try / finally中)
1 | latch.countDown(); |
1 2 3 4 | ExecutorService taskExecutor = Executors.newFixedThreadPool(4); List<Callable< ? >> tasks; // your tasks // invokeAll() returns when all tasks are complete List<Future< ? >> futures = taskExecutor.invokeAll(tasks); |
您还可以使用期货清单:
1 2 3 4 5 6 7 8 | List<Future> futures = new ArrayList<Future>(); // now add to it: futures.add(executorInstance.submit(new Callable<Void>() { public Void call() throws IOException { // do something return null; } })); |
然后,当你想要加入所有这些时,它基本上相当于加入每个,(带来额外的好处,它将子线程的异常重新引发到main):
1 | for(Future f: this.futures) { f.get(); } |
基本上诀窍是一次调用每个Future上的.get(),而不是无限循环调用isDone()on(all或each)。因此,一旦最后一个线程完成,您就可以保证"继续"前进并经过此块。需要注意的是,由于.get()调用会重新引发异常,如果其中一个线程死掉,你可能会在其他线程完成之前从这里加注[为了避免这种情况,你可以添加一个
在Java8中,您可以使用CompletableFuture执行此操作:
1 2 3 4 5 6 7 | ExecutorService es = Executors.newFixedThreadPool(4); List<Runnable> tasks = getTasks(); CompletableFuture< ? >[] futures = tasks.stream() .map(task -> CompletableFuture.runAsync(task, es)) .toArray(CompletableFuture[]::new); CompletableFuture.allOf(futures).join(); es.shutdown(); |
只是我的两分钱。
为了克服
1 2 3 4 5 6 7 8 9 10 11 | ExecutorService taskExecutor = Executors.newFixedThreadPool(4); int numberOfTasks=0; Semaphore s=new Semaphore(0); while(...) { taskExecutor.execute(new MyTask()); numberOfTasks++; } try { s.aquire(numberOfTasks); ... |
在你的任务中,只需像
游戏有点晚了但是为了完成...
而不是"等待"所有任务完成,你可以考虑好莱坞原则,"不要打电话给我,我会打电话给你" - 当我完成时。
我认为结果代码更优雅......
Guava提供了一些有趣的工具来实现这一目标。
一个例子 ::
将ExecutorService包装到ListeningExecutorService ::
1 | ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newFixedThreadPool(10)); |
提交一系列可执行的callables ::
1 2 3 4 5 | for (Callable<Integer> callable : callables) { ListenableFuture<Integer> lf = service.submit(callable); // listenableFutures is a collection listenableFutures.add(lf) }); |
现在必不可少的部分:
1 | ListenableFuture<List<Integer>> lf = Futures.successfulAsList(listenableFutures); |
附加一个回调给ListenableFuture,你可以用来在所有期货完成时收到通知::
1 2 3 4 5 6 7 8 9 10 11 12 | Futures.addCallback(lf, new FutureCallback<List<Integer>>() { @Override public void onSuccess(List<Integer> result) { log.info("@@ finished processing {} elements", Iterables.size(result)); // do something with all the results } @Override public void onFailure(Throwable t) { log.info("@@ failed because of :: {}", t); } }); |
这也提供了一个优点,您可以在处理完成后在一个地方收集所有结果...
更多信息在这里
Java 5及更高版本中的CyclicBarrier类是为此类设计的。
请遵循以下方法之一。
相关的SE问题:
CountDownLatch如何在Java多线程中使用?
如何正确关闭java ExecutorService
这里有两个选项,只是有点混淆哪一个最好去。
选项1:
1 2 3 4 5 6 7 | ExecutorService es = Executors.newFixedThreadPool(4); List<Runnable> tasks = getTasks(); CompletableFuture< ? >[] futures = tasks.stream() .map(task -> CompletableFuture.runAsync(task, es)) .toArray(CompletableFuture[]::new); CompletableFuture.allOf(futures).join(); es.shutdown(); |
选项2:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
这里放下future.get();在尝试捕获是好主意吗?
你可以将你的任务包装在另一个runnable中,它将发送通知:
1 2 3 4 5 6 7 | taskExecutor.execute(new Runnable() { public void run() { taskStartedNotification(); new MyTask().run(); taskFinishedNotification(); } }); |
我刚刚编写了一个示例程序来解决您的问题。没有给出简洁的实现,所以我将添加一个。虽然您可以使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | ExecutorService es = Executors.newCachedThreadPool(); List<Callable<Integer>> tasks = new ArrayList<>(); for (int j = 1; j <= 10; j++) { tasks.add(new Callable<Integer>() { @Override public Integer call() throws Exception { int sum = 0; System.out.println("Starting Thread" + Thread.currentThread().getId()); for (int i = 0; i < 1000000; i++) { sum += i; } System.out.println("Stopping Thread" + Thread.currentThread().getId()); return sum; } }); } try { List<Future<Integer>> futures = es.invokeAll(tasks); int flag = 0; for (Future<Integer> f : futures) { Integer res = f.get(); System.out.println("Sum:" + res); if (!f.isDone()) flag = 1; } if (flag == 0) System.out.println("SUCCESS"); else System.out.println("FAILED"); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } |
只是为了提供更多替代方案,使用闩锁/障碍物。
您还可以获得部分结果,直到所有结果都使用CompletionService完成。
从Java Concurrency实践中:
"如果您有一批计算要提交给执行者,并且您希望在结果成功时检索他们的结果
可用,您可以保留与每个任务关联的Future,并通过调用get来重复轮询完成
超时为零。这是可能的,但很乏味。幸运的是,有一种更好的方式:完成服务。"
在这里实施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | public class TaskSubmiter { private final ExecutorService executor; TaskSubmiter(ExecutorService executor) { this.executor = executor; } void doSomethingLarge(AnySourceClass source) { final List<InterestedResult> info = doPartialAsyncProcess(source); CompletionService<PartialResult> completionService = new ExecutorCompletionService<PartialResult>(executor); for (final InterestedResult interestedResultItem : info) completionService.submit(new Callable<PartialResult>() { public PartialResult call() { return InterestedResult.doAnOperationToGetPartialResult(); } }); try { for (int t = 0, n = info.size(); t < n; t++) { Future<PartialResult> f = completionService.take(); PartialResult PartialResult = f.get(); processThisSegment(PartialResult); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (ExecutionException e) { throw somethinghrowable(e.getCause()); } } } |
你可以使用这段代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class MyTask implements Runnable { private CountDownLatch countDownLatch; public MyTask(CountDownLatch countDownLatch { this.countDownLatch = countDownLatch; } @Override public void run() { try { //Do somethings // this.countDownLatch.countDown();//important } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } CountDownLatch countDownLatch = new CountDownLatch(NUMBER_OF_TASKS); ExecutorService taskExecutor = Executors.newFixedThreadPool(4); for (int i = 0; i < NUMBER_OF_TASKS; i++){ taskExecutor.execute(new MyTask(countDownLatch)); } countDownLatch.await(); System.out.println("Finish tasks"); |
这是我的解决方案,基于"AdamSkywalker"提示,它的工作原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | package frss.main; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestHilos { void procesar() { ExecutorService es = Executors.newFixedThreadPool(4); List<Runnable> tasks = getTasks(); CompletableFuture< ? >[] futures = tasks.stream().map(task -> CompletableFuture.runAsync(task, es)).toArray(CompletableFuture[]::new); CompletableFuture.allOf(futures).join(); es.shutdown(); System.out.println("FIN DEL PROCESO DE HILOS"); } private List<Runnable> getTasks() { List<Runnable> tasks = new ArrayList<Runnable>(); Hilo01 task1 = new Hilo01(); tasks.add(task1); Hilo02 task2 = new Hilo02(); tasks.add(task2); return tasks; } private class Hilo01 extends Thread { @Override public void run() { System.out.println("HILO 1"); } } private class Hilo02 extends Thread { @Override public void run() { try { sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("HILO 2"); } } public static void main(String[] args) { TestHilos test = new TestHilos(); test.procesar(); } } |
我创建了以下工作示例。我们的想法是有一种方法来处理一个任务池(我使用队列作为示例)和许多线程(通过numberOfTasks / threshold以编程方式确定),并等到所有线程完成后继续进行其他一些处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | import java.util.PriorityQueue; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** Testing CountDownLatch and ExecutorService to manage scenario where * multiple Threads work together to complete tasks from a single * resource provider, so the processing can be faster. */ public class ThreadCountDown { private CountDownLatch threadsCountdown = null; private static Queue<Integer> tasks = new PriorityQueue<>(); public static void main(String[] args) { // Create a queue with"Tasks" int numberOfTasks = 2000; while(numberOfTasks-- > 0) { tasks.add(numberOfTasks); } // Initiate Processing of Tasks ThreadCountDown main = new ThreadCountDown(); main.process(tasks); } /* Receiving the Tasks to process, and creating multiple Threads * to process in parallel. */ private void process(Queue<Integer> tasks) { int numberOfThreads = getNumberOfThreadsRequired(tasks.size()); threadsCountdown = new CountDownLatch(numberOfThreads); ExecutorService threadExecutor = Executors.newFixedThreadPool(numberOfThreads); //Initialize each Thread while(numberOfThreads-- > 0) { System.out.println("Initializing Thread:"+numberOfThreads); threadExecutor.execute(new MyThread("Thread"+numberOfThreads)); } try { //Shutdown the Executor, so it cannot receive more Threads. threadExecutor.shutdown(); threadsCountdown.await(); System.out.println("ALL THREADS COMPLETED!"); //continue With Some Other Process Here } catch (InterruptedException ex) { ex.printStackTrace(); } } /* Determine the number of Threads to create */ private int getNumberOfThreadsRequired(int size) { int threshold = 100; int threads = size / threshold; if( size > (threads*threshold) ){ threads++; } return threads; } /* Task Provider. All Threads will get their task from here */ private synchronized static Integer getTask(){ return tasks.poll(); } /* The Threads will get Tasks and process them, while still available. * When no more tasks available, the thread will complete and reduce the threadsCountdown */ private class MyThread implements Runnable { private String threadName; protected MyThread(String threadName) { super(); this.threadName = threadName; } @Override public void run() { Integer task; try{ //Check in the Task pool if anything pending to process while( (task = getTask()) != null ){ processTask(task); } }catch (Exception ex){ ex.printStackTrace(); }finally { /*Reduce count when no more tasks to process. Eventually all Threads will end-up here, reducing the count to 0, allowing the flow to continue after threadsCountdown.await(); */ threadsCountdown.countDown(); } } private void processTask(Integer task){ try{ System.out.println(this.threadName+" is Working on Task:"+ task); }catch (Exception ex){ ex.printStackTrace(); } } } } |
希望能帮助到你!
你应该使用
一个例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class ScheduledThreadPoolExample { public static void main(String[] args) throws InterruptedException { ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5); executorService.scheduleAtFixedRate(() -> System.out.println("process task."), 0, 1, TimeUnit.SECONDS); TimeUnit.SECONDS.sleep(10); executorService.shutdown(); executorService.awaitTermination(1, TimeUnit.DAYS); } } |
Java 8 - 我们可以使用流API来处理流。请参阅下面的代码段
1 2 3 4 5 6 7 |
所以我在这里发布链接问题的答案,因为有人想要一个更简单的方法来做到这一点
1 2 3 4 5 6 7 8 | ExecutorService executor = Executors.newFixedThreadPool(10); CompletableFuture[] futures = new CompletableFuture[10]; int i = 0; while (...) { futures[i++] = CompletableFuture.runAsync(runner, executor); } CompletableFuture.allOf(futures).join(); // THis will wait until all future ready. |
您可以使用自己的ExecutorCompletionService子类来包装
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | ExecutorService WORKER_THREAD_POOL = Executors.newFixedThreadPool(10); CountDownLatch latch = new CountDownLatch(2); for (int i = 0; i < 2; i++) { WORKER_THREAD_POOL.submit(() -> { try { // doSomething(); latch.countDown(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); } // wait for the latch to be decremented by the two remaining threads latch.await(); |
如果
你可以在这个Runner类上调用waitTillDone():
1 2 3 4 5 6 7 8 9 10 11 | Runner runner = Runner.runner(4); // create pool with 4 threads in thread pool while(...) { runner.run(new MyTask()); // here you submit your task } runner.waitTillDone(); // and this blocks until all tasks are finished (or failed) runner.shutdown(); // once you done you can shutdown the runner |
您可以重用此类并在调用shutdown()之前多次调用waitTillDone(),此外您的代码非常简单。此外,您不必预先知道任务的数量。
要使用它,只需将此gradle / maven
更多详情可在这找到:
https://github.com/MatejTymes/JavaFixes
这可能有所帮助
1 2 3 4 5 6 7 8 9 10 11 12 | Log.i(LOG_TAG,"shutting down executor..."); executor.shutdown(); while (true) { try { Log.i(LOG_TAG,"Waiting for executor to terminate..."); if (executor.isTerminated()) break; if (executor.awaitTermination(5000, TimeUnit.MILLISECONDS)) { break; } } catch (InterruptedException ignored) {} } |
在executor
在跨越线程后,我们可以检查
1 2 3 4 5 6 | while (true) { if (executor.getActiveCount() == 0) { //ur own piece of code break; } } |