[英]How to wait for a number of threads to complete?
什么是简单地等待所有线程进程完成的方法? 例如,假设我有:
public class DoSomethingInAThread implements Runnable{
public static void main(String[] args) {
for (int n=0; n<1000; n++) {
Thread t = new Thread(new DoSomethingInAThread());
t.start();
}
// wait for all threads' run() methods to complete before continuing
}
public void run() {
// do something here
}
}
我如何更改它以便main()
方法在注释处暂停,直到所有线程的run()
方法退出? 谢谢!
你把所有线程放在一个数组中,启动它们,然后有一个循环
for(i = 0; i < threads.length; i++)
threads[i].join();
每个连接都会阻塞,直到相应的线程完成。 线程的完成顺序可能与您加入它们的顺序不同,但这不是问题:当循环退出时,所有线程都已完成。
一种方法是创建一个Thread
List
,创建并启动每个线程,同时将其添加到列表中。 启动所有内容后,循环返回列表并在每个列表上调用join()
。 线程完成执行的顺序无关紧要,您需要知道的是,当第二个循环完成执行时,每个线程都将完成。
更好的方法是使用ExecutorService及其相关方法:
List<Callable> callables = ... // assemble list of Callables here
// Like Runnable but can return a value
ExecutorService execSvc = Executors.newCachedThreadPool();
List<Future<?>> results = execSvc.invokeAll(callables);
// Note: You may not care about the return values, in which case don't
// bother saving them
使用 ExecutorService(以及 Java 5并发实用程序中的所有新内容)非常灵活,上面的示例几乎没有触及表面。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class DoSomethingInAThread implements Runnable
{
public static void main(String[] args) throws ExecutionException, InterruptedException
{
//limit the number of actual threads
int poolSize = 10;
ExecutorService service = Executors.newFixedThreadPool(poolSize);
List<Future<Runnable>> futures = new ArrayList<Future<Runnable>>();
for (int n = 0; n < 1000; n++)
{
Future f = service.submit(new DoSomethingInAThread());
futures.add(f);
}
// wait for all tasks to complete before continuing
for (Future<Runnable> f : futures)
{
f.get();
}
//shut down the executor service so that this thread can exit
service.shutdownNow();
}
public void run()
{
// do something here
}
}
您可以使用CountDownLatch代替旧 API 的join()
。 我已将您的代码修改如下以满足您的要求。
import java.util.concurrent.*;
class DoSomethingInAThread implements Runnable{
CountDownLatch latch;
public DoSomethingInAThread(CountDownLatch latch){
this.latch = latch;
}
public void run() {
try{
System.out.println("Do some thing");
latch.countDown();
}catch(Exception err){
err.printStackTrace();
}
}
}
public class CountDownLatchDemo {
public static void main(String[] args) {
try{
CountDownLatch latch = new CountDownLatch(1000);
for (int n=0; n<1000; n++) {
Thread t = new Thread(new DoSomethingInAThread(latch));
t.start();
}
latch.await();
System.out.println("In Main thread after completion of 1000 threads");
}catch(Exception err){
err.printStackTrace();
}
}
}
说明:
CountDownLatch
已根据您的要求使用给定的计数 1000 进行初始化。
每个工作线程DoSomethingInAThread
将递减CountDownLatch
,它已在构造函数中传递。
主线程CountDownLatchDemo
await()
直到计数变为零。 一旦计数变为零,您将在输出中获得以下行。
In Main thread after completion of 1000 threads
来自 oracle 文档页面的更多信息
public void await()
throws InterruptedException
使当前线程等待直到闩锁倒计时为零,除非线程被中断。
有关其他选项,请参阅相关的 SE 问题:
完全避免使用 Thread 类,而是使用 java.util.concurrent 中提供的更高抽象
ExecutorService 类提供的方法 invokeAll似乎可以满足您的需求。
考虑使用java.util.concurrent.CountDownLatch
。 javadoc 中的示例
正如 Martin K 建议的那样, java.util.concurrent.CountDownLatch
似乎是一个更好的解决方案。 只需添加一个相同的示例
public class CountDownLatchDemo
{
public static void main (String[] args)
{
int noOfThreads = 5;
// Declare the count down latch based on the number of threads you need
// to wait on
final CountDownLatch executionCompleted = new CountDownLatch(noOfThreads);
for (int i = 0; i < noOfThreads; i++)
{
new Thread()
{
@Override
public void run ()
{
System.out.println("I am executed by :" + Thread.currentThread().getName());
try
{
// Dummy sleep
Thread.sleep(3000);
// One thread has completed its job
executionCompleted.countDown();
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}.start();
}
try
{
// Wait till the count down latch opens.In the given case till five
// times countDown method is invoked
executionCompleted.await();
System.out.println("All over");
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
根据您的需要,您可能还想查看 java.util.concurrent 包中的 CountDownLatch 和 CyclicBarrier 类。 如果您希望线程相互等待,或者希望对线程执行方式进行更细粒度的控制(例如,在它们的内部执行中等待另一个线程设置某些状态),它们会很有用。 您还可以使用 CountDownLatch 来通知所有线程同时启动,而不是在循环遍历时一个一个地启动它们。 标准 API 文档有一个这样的例子,另外还使用另一个 CountDownLatch 来等待所有线程完成它们的执行。
如果您列出线程,您可以遍历它们并对每个线程执行 .join() ,当所有线程都完成时,您的循环将结束。 不过我没试过。
http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join()
这将是一条评论,但我还不能发表评论。
Martin K ,我很好奇您如何使用ThreadGroup
。 你以前做过吗?
我看到上面的,你建议检查activeCount
-除了撇开马丁 五世 Löwis的关切投票的那一刻,我有另一个值得关注的activeCount
本身。
警告:我还没有尝试使用它,所以我对此不是专家,但是根据javadocs ,它返回活动线程数的估计值 。
就我个人而言,我不愿意尝试在估计的基础上建立一个系统。 您是否还有其他想法,还是我误读了Javadoc?
在第一个 for 循环内创建线程对象。
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(new Runnable() {
public void run() {
// some code to run in parallel
}
});
threads[i].start();
}
那么这里的每个人都在说什么。
for(i = 0; i < threads.length; i++)
threads[i].join();
作为CountDownLatch的替代方案,您还可以使用CyclicBarrier例如
public class ThreadWaitEx {
static CyclicBarrier barrier = new CyclicBarrier(100, new Runnable(){
public void run(){
System.out.println("clean up job after all tasks are done.");
}
});
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
Thread t = new Thread(new MyCallable(barrier));
t.start();
}
}
}
class MyCallable implements Runnable{
private CyclicBarrier b = null;
public MyCallable(CyclicBarrier b){
this.b = b;
}
@Override
public void run(){
try {
//do something
System.out.println(Thread.currentThread().getName()+" is waiting for barrier after completing his job.");
b.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
要在这种情况下使用 CyclicBarrier,barrier.await() 应该是最后一个语句,即当您的线程完成其工作时。 CyclicBarrier 可以通过其 reset() 方法再次使用。 引用 javadocs:
CyclicBarrier 支持可选的 Runnable 命令,该命令在每个屏障点运行一次,在派对中的最后一个线程到达之后,但在任何线程被释放之前。 此屏障操作对于在任何一方继续之前更新共享状态很有用。
join()
对我没有帮助。 在 Kotlin 中查看此示例:
val timeInMillis = System.currentTimeMillis()
ThreadUtils.startNewThread(Runnable {
for (i in 1..5) {
val t = Thread(Runnable {
Thread.sleep(50)
var a = i
kotlin.io.println(Thread.currentThread().name + "|" + "a=$a")
Thread.sleep(200)
for (j in 1..5) {
a *= j
Thread.sleep(100)
kotlin.io.println(Thread.currentThread().name + "|" + "$a*$j=$a")
}
kotlin.io.println(Thread.currentThread().name + "|TaskDurationInMillis = " + (System.currentTimeMillis() - timeInMillis))
})
t.start()
}
})
结果:
Thread-5|a=5
Thread-1|a=1
Thread-3|a=3
Thread-2|a=2
Thread-4|a=4
Thread-2|2*1=2
Thread-3|3*1=3
Thread-1|1*1=1
Thread-5|5*1=5
Thread-4|4*1=4
Thread-1|2*2=2
Thread-5|10*2=10
Thread-3|6*2=6
Thread-4|8*2=8
Thread-2|4*2=4
Thread-3|18*3=18
Thread-1|6*3=6
Thread-5|30*3=30
Thread-2|12*3=12
Thread-4|24*3=24
Thread-4|96*4=96
Thread-2|48*4=48
Thread-5|120*4=120
Thread-1|24*4=24
Thread-3|72*4=72
Thread-5|600*5=600
Thread-4|480*5=480
Thread-3|360*5=360
Thread-1|120*5=120
Thread-2|240*5=240
Thread-1|TaskDurationInMillis = 765
Thread-3|TaskDurationInMillis = 765
Thread-4|TaskDurationInMillis = 765
Thread-5|TaskDurationInMillis = 765
Thread-2|TaskDurationInMillis = 765
现在让我将join()
用于线程:
val timeInMillis = System.currentTimeMillis()
ThreadUtils.startNewThread(Runnable {
for (i in 1..5) {
val t = Thread(Runnable {
Thread.sleep(50)
var a = i
kotlin.io.println(Thread.currentThread().name + "|" + "a=$a")
Thread.sleep(200)
for (j in 1..5) {
a *= j
Thread.sleep(100)
kotlin.io.println(Thread.currentThread().name + "|" + "$a*$j=$a")
}
kotlin.io.println(Thread.currentThread().name + "|TaskDurationInMillis = " + (System.currentTimeMillis() - timeInMillis))
})
t.start()
t.join()
}
})
结果:
Thread-1|a=1
Thread-1|1*1=1
Thread-1|2*2=2
Thread-1|6*3=6
Thread-1|24*4=24
Thread-1|120*5=120
Thread-1|TaskDurationInMillis = 815
Thread-2|a=2
Thread-2|2*1=2
Thread-2|4*2=4
Thread-2|12*3=12
Thread-2|48*4=48
Thread-2|240*5=240
Thread-2|TaskDurationInMillis = 1568
Thread-3|a=3
Thread-3|3*1=3
Thread-3|6*2=6
Thread-3|18*3=18
Thread-3|72*4=72
Thread-3|360*5=360
Thread-3|TaskDurationInMillis = 2323
Thread-4|a=4
Thread-4|4*1=4
Thread-4|8*2=8
Thread-4|24*3=24
Thread-4|96*4=96
Thread-4|480*5=480
Thread-4|TaskDurationInMillis = 3078
Thread-5|a=5
Thread-5|5*1=5
Thread-5|10*2=10
Thread-5|30*3=30
Thread-5|120*4=120
Thread-5|600*5=600
Thread-5|TaskDurationInMillis = 3833
当我们使用join
时很明显:
我们防止阻塞其他线程的解决方案是创建一个 ArrayList:
val threads = ArrayList<Thread>()
现在,当我们想要启动一个新线程时,我们最常将其添加到 ArrayList 中:
addThreadToArray(
ThreadUtils.startNewThread(Runnable {
...
})
)
addThreadToArray
函数:
@Synchronized
fun addThreadToArray(th: Thread) {
threads.add(th)
}
startNewThread
函数:
fun startNewThread(runnable: Runnable) : Thread {
val th = Thread(runnable)
th.isDaemon = false
th.priority = Thread.MAX_PRIORITY
th.start()
return th
}
在需要的地方检查线程的完成情况,如下所示:
val notAliveThreads = ArrayList<Thread>()
for (t in threads)
if (!t.isAlive)
notAliveThreads.add(t)
threads.removeAll(notAliveThreads)
if (threads.size == 0){
// The size is 0 -> there is no alive threads.
}
问题在于:
for(i = 0; i < threads.length; i++)
threads[i].join();
...是, threads[i + 1]
永远不能在threads[i]
之前加入。 除了“闩锁”的那些,所有的解决方案都有这个缺陷。
这里(还)没有人提到ExecutorCompletionService ,它允许根据完成顺序加入线程/任务:
public class ExecutorCompletionService<V>
extends Object
implements CompletionService<V>
CompletionService
使用提供的Executor
来执行任务。 此类安排提交的任务在完成后放置在使用 take 可访问的队列中。 该类足够轻量级,适合在处理任务组时临时使用。用法示例。
假设您有一组针对某个问题的求解器,每个求解器都返回某种 Result 类型的值,并且希望并发运行它们,处理它们中每个返回非空值的结果,在某些方法中
use(Result r)
。 你可以这样写:void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException, ExecutionException { CompletionService<Result> cs = new ExecutorCompletionService<>(e); solvers.forEach(cs::submit); for (int i = solvers.size(); i > 0; i--) { Result r = cs.take().get(); if (r != null) use(r); } }
假设您想使用任务集的第一个非空结果,忽略任何遇到异常的结果,并在第一个任务准备好时取消所有其他任务:
void solve(Executor e, Collection<Callable<Result>> solvers) throws InterruptedException { CompletionService<Result> cs = new ExecutorCompletionService<>(e); int n = solvers.size(); List<Future<Result>> futures = new ArrayList<>(n); Result result = null; try { solvers.forEach(solver -> futures.add(cs.submit(solver))); for (int i = n; i > 0; i--) { try { Result r = cs.take().get(); if (r != null) { result = r; break; } } catch (ExecutionException ignore) {} } } finally { futures.forEach(future -> future.cancel(true)); } if (result != null) use(result); }
从:1.5 (!)
假设use(r)
(例 1 的)也是异步的,我们有很大的优势。 #
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.