等到所有的线程在java中完成他们的工作

我正在编写一个应用程序,它有5个线程同时从Web获取一些信息,并在缓冲区类中填充5个不同的字段。
当所有线程完成他们的工作时,我需要validation缓冲区数据并将其存储在数据库中。
我怎样才能做到这一点(当所有的线程完成他们的工作时得到提醒)?

我采取的方法是使用ExecutorService来pipe理线程池。

ExecutorService es = Executors.newCachedThreadPool(); for(int i=0;i<5;i++) es.execute(new Runnable() { /* your task */ }); es.shutdown(); boolean finshed = es.awaitTermination(1, TimeUnit.MINUTES); // all tasks have finished or the time has been reached. 

你可以join到线程中。 连接阻塞,直到线程完成。

 for (Thread thread : threads) { thread.join(); } 

请注意, join会引发InterruptedException 。 如果发生这种情况,你将不得不决定做什么(例如,尝试取消其他线程以防止不必要的工作)。

看看各种解决scheme。

  1. join() API已经在早期版本的Java中引入。 自从JDK 1.5发布以来,这个并发包提供了一些很好的select。

  2. ExecutorService中的invokeAll#()

    执行给定的任务,返回一个完整的状态和结果的期货清单。

    代码示例请参考此相关的SE问题:

    如何使用invokeAll()让所有线程池完成他们的任务?

  3. CountDownLatch

    同步协助,允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

    CountDownLatch用给定的计数初始化。 await方法阻塞,直到当前计数由于调用countDown()方法而达到零,在此之后所有等待的线程被释放,并且任何后续的调用立即返回。 这是一次性现象 – 计数不能被重置。 如果您需要重置计数的版本,请考虑使用CyclicBarrier

    请参阅此问题以了解CountDownLatch用法

    如何等待一个线程,产生自己的线程?

  4. ForkJoinPool或newWorkStealingPool()在执行程序中

  5. 在提交到ExecutorService后迭代所有Future对象

除了别人提出的Thread.join()之外,java 5还引入了executor框架。 在那里你不用Thread对象。 而是将您的CallableRunnable对象提交给执行程序。 有一个特殊的执行程序是为了执行多个任务,并将其结果无序地返回。 这是ExecutorCompletionService

 ExecutorCompletionService executor; for (..) { executor.submit(Executors.callable(yourRunnable)); } 

然后你可以重复调用take()直到没有更多的Future<?>对象返回,这意味着所有的都完成了。


另一件可能相关的事情,根据你的情况是CyclicBarrier

同步辅助,允许一组线程全部等待对方达到共同的障碍点。 CyclicBarriers在涉及固定大小的线程的程序中很有用,它必须偶尔等待对方。 这个屏障被称为循环的,因为它可以在等待的线程被释放之后重新使用。

另一种可能性是CountDownLatch对象,它对于简单的情况很有用:因为您事先知道线程的数量,所以您使用相关的计数初始化它,并将对象的引用传递给每个线程。
完成任务后,每个线程调用CountDownLatch.countDown() ,递减内部计数器。 主线程在启动所有其他线程之后,应该执行CountDownLatch.await()阻塞调用。 只要内部计数器达到0,它就会被释放。

请注意,使用这个对象, InterruptedException也会被抛出。

你做

 for (Thread t : new Thread[] { th1, th2, th3, th4, th5 }) t.join() 

在这个循环之后,你可以确定所有的线程都完成了他们的工作。

将线程对象存储到某个集合(如List或Set)中,然后在线程启动后循环集合,并在线程上调用join() 。

执行者服务可以用来pipe理多个线程,包括状态和完成。 请参阅http://programmingexamples.wikidot.com/executorservice

试试这个,会起作用。

  Thread[] threads = new Thread[10]; List<Thread> allThreads = new ArrayList<Thread>(); for(Thread thread : threads){ if(null != thread){ if(thread.isAlive()){ allThreads.add(thread); } } } while(!allThreads.isEmpty()){ Iterator<Thread> ite = allThreads.iterator(); while(ite.hasNext()){ Thread thread = ite.next(); if(!thread.isAlive()){ ite.remove(); } } } 

你可以使用Threadf#join方法来达到这个目的。

虽然与OP的问题没有关系,但如果您只对一个线程同步(更确切地说是一个rendez-vous),您可以使用一个交换器

在我的情况下,我需要暂停父线程,直到子线程做了一些事情,例如完成初始化。 CountDownLatch也很好。

我有一个类似的问题,并最终使用Java 8 parallelStream。

 requestList.parallelStream().forEach(req -> makeRequest(req)); 

这是超级简单和可读。 在幕后,它使用默认的JVM的fork连接池,这意味着它将在继续之前等待所有的线程完成。 对我来说这是一个很好的解决scheme,因为它是我的应用程序中唯一的parallelStream。 如果您有多个parallelStream同时运行,请阅读下面的链接。

有关并行stream的更多信息

在你的主线程中使用这个:while(!executor.isTerminated()); 从执行程序服务启动所有线程后,放置这一行代码。 这将只在执行程序启动的所有线程完成后才启动主线程。 确保调用executor.shutdown(); 在上面的循环之前。