简体   繁体   English

如何等待多个线程完成?

[英]How to wait for a number of threads to complete?

What is a way to simply wait for all threaded process to finish?什么是简单地等待所有线程进程完成的方法? For example, let's say I have:例如,假设我有:

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
    }


}

How do I alter this so the main() method pauses at the comment until all threads' run() methods exit?我如何更改它以便main()方法在注释处暂停,直到所有线程的run()方法退出? Thanks!谢谢!

You put all threads in an array, start them all, and then have a loop你把所有线程放在一个数组中,启动它们,然后有一个循环

for(i = 0; i < threads.length; i++)
  threads[i].join();

Each join will block until the respective thread has completed.每个连接都会阻塞,直到相应的线程完成。 Threads may complete in a different order than you joining them, but that's not a problem: when the loop exits, all threads are completed.线程的完成顺序可能与您加入它们的顺序不同,但这不是问题:当循环退出时,所有线程都已完成。

One way would be to make a List of Thread s, create and launch each thread, while adding it to the list.一种方法是创建一个Thread List ,创建并启动每个线程,同时将其添加到列表中。 Once everything is launched, loop back through the list and call join() on each one.启动所有内容后,循环返回列表并在每个列表上调用join() It doesn't matter what order the threads finish executing in, all you need to know is that by the time that second loop finishes executing, every thread will have completed.线程完成执行的顺序无关紧要,您需要知道的是,当第二个循环完成执行时,每个线程都将完成。

A better approach is to use an ExecutorService and its associated methods:更好的方法是使用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

Using an ExecutorService (and all of the new stuff from Java 5's concurrency utilities ) is incredibly flexible, and the above example barely even scratches the surface.使用 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
   }
}

instead of join() , which is an old API, you can use CountDownLatch .您可以使用CountDownLatch代替旧 API 的join() I have modified your code as below to fulfil your requirement.我已将您的代码修改如下以满足您的要求。

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();
        }
    }
}

Explanation :说明

  1. CountDownLatch has been initialized with given count 1000 as per your requirement. CountDownLatch已根据您的要求使用给定的计数 1000 进行初始化。

  2. Each worker thread DoSomethingInAThread will decrement the CountDownLatch , which has been passed in constructor.每个工作线程DoSomethingInAThread将递减CountDownLatch ,它已在构造函数中传递。

  3. Main thread CountDownLatchDemo await() till the count has become zero.主线程CountDownLatchDemo await()直到计数变为零。 Once the count has become zero, you will get below line in output.一旦计数变为零,您将在输出中获得以下行。

     In Main thread after completion of 1000 threads

More info from oracle documentation page来自 oracle 文档页面的更多信息

public void await()
           throws InterruptedException

Causes the current thread to wait until the latch has counted down to zero, unless the thread is interrupted.使当前线程等待直到闩锁倒计时为零,除非线程被中断。

Refer to related SE question for other options:有关其他选项,请参阅相关的 SE 问题:

wait until all threads finish their work in java 等到所有线程在 java 中完成它们的工作

Avoid the Thread class altogether and instead use the higher abstractions provided in java.util.concurrent完全避免使用 Thread 类,而是使用 java.util.concurrent 中提供的更高抽象

The ExecutorService class provides the method invokeAll that seems to do just what you want. ExecutorService 类提供的方法 invokeAll似乎可以满足您的需求。

Consider using java.util.concurrent.CountDownLatch .考虑使用java.util.concurrent.CountDownLatch Examples in javadocs javadoc 中的示例

As Martin K suggested java.util.concurrent.CountDownLatch seems to be a better solution for this.正如 Martin K 建议的那样, java.util.concurrent.CountDownLatch似乎是一个更好的解决方案。 Just adding an example for the same只需添加一个相同的示例

     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();
        }
    }

}

Depending on your needs, you may also want to check out the classes CountDownLatch and CyclicBarrier in the java.util.concurrent package.根据您的需要,您可能还想查看 java.util.concurrent 包中的 CountDownLatch 和 CyclicBarrier 类。 They can be useful if you want your threads to wait for each other, or if you want more fine-grained control over the way your threads execute (eg, waiting in their internal execution for another thread to set some state).如果您希望线程相互等待,或者希望对线程执行方式进行更细粒度的控制(例如,在它们的内部执行中等待另一个线程设置某些状态),它们会很有用。 You could also use a CountDownLatch to signal all of your threads to start at the same time, instead of starting them one by one as you iterate through your loop.您还可以使用 CountDownLatch 来通知所有线程同时启动,而不是在循环遍历时一个一个地启动它们。 The standard API docs have an example of this, plus using another CountDownLatch to wait for all threads to complete their execution.标准 API 文档有一个这样的例子,另外还使用另一个 CountDownLatch 来等待所有线程完成它们的执行。

If you make a list of the threads, you can loop through them and .join() against each, and your loop will finish when all the threads have.如果您列出线程,您可以遍历它们并对每个线程执行 .join() ,当所有线程都完成时,您的循环将结束。 I haven't tried it though.不过我没试过。

http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join() http://docs.oracle.com/javase/8/docs/api/java/lang/Thread.html#join()

This would be a comment, but I can't do comments yet. 这将是一条评论,但我还不能发表评论。

Martin K , I'm curious about how you'd use ThreadGroup . Martin K ,我很好奇您如何使用ThreadGroup Have you done so before? 你以前做过吗?

I see that above, you suggest checking activeCount - setting aside Martin v Löwis 's concern about polling aside for the moment, I have another concern with activeCount itself. 我看到上面的,你建议检查activeCount -除了撇开马丁 五世 Löwis的关切投票的那一刻,我有另一个值得关注的activeCount本身。

Caveat: I haven't tried using this, so I'm no expert on the matter, but according to the javadocs , it returns an estimate of the number of active threads. 警告:我还没有尝试使用它,所以我对此不是专家,但是根据javadocs ,它返回活动线程数的估计值

Personally, I'd be loathe to try and build a system on an estimate. 就我个人而言,我不愿意尝试在估计的基础上建立一个系统。 Do you have another thought on how to do it, or am I misreading the javadoc? 您是否还有其他想法,还是我误读了Javadoc?

Create the thread object inside the first for loop.在第一个 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();
 }

And then so what everyone here is saying.那么这里的每个人都在说什么。

for(i = 0; i < threads.length; i++)
  threads[i].join();

As an alternative to CountDownLatch you can also use CyclicBarrier eg作为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();
        }
    }       
}

To use CyclicBarrier in this case barrier.await() should be the last statement ie when your thread is done with its job.要在这种情况下使用 CyclicBarrier,barrier.await() 应该是最后一个语句,即当您的线程完成其工作时。 CyclicBarrier can be used again with its reset() method. CyclicBarrier 可以通过其 reset() 方法再次使用。 To quote javadocs:引用 javadocs:

A CyclicBarrier supports an optional Runnable command that is run once per barrier point, after the last thread in the party arrives, but before any threads are released. CyclicBarrier 支持可选的 Runnable 命令,该命令在每个屏障点运行一次,在派对中的最后一个线程到达之后,但在任何线程被释放之前。 This barrier action is useful for updating shared-state before any of the parties continue.此屏障操作对于在任何一方继续之前更新共享状态很有用。

The join() was not helpful to me. join()对我没有帮助。 see this sample in Kotlin:在 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()
        }
    })

The result:结果:

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

Now let me use the join() for threads:现在让我将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()
        }
    })

And the result:结果:

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

As it's clear when we use the join :当我们使用join时很明显:

  1. The threads are running sequentially.线程按顺序运行。
  2. The first sample takes 765 Milliseconds while the second sample takes 3833 Milliseconds.第一个样本需要 765 毫秒,而第二个样本需要 3833 毫秒。

Our solution to prevent blocking other threads was creating an ArrayList:我们防止阻塞其他线程的解决方案是创建一个 ArrayList:

val threads = ArrayList<Thread>()

Now when we want to start a new thread we most add it to the ArrayList:现在,当我们想要启动一个新线程时,我们最常将其添加到 ArrayList 中:

addThreadToArray(
    ThreadUtils.startNewThread(Runnable {
        ...
    })
)

The addThreadToArray function: addThreadToArray函数:

@Synchronized
fun addThreadToArray(th: Thread) {
    threads.add(th)
}

The startNewThread funstion: startNewThread函数:

fun startNewThread(runnable: Runnable) : Thread {
    val th = Thread(runnable)
    th.isDaemon = false
    th.priority = Thread.MAX_PRIORITY
    th.start()
    return th
}

Check the completion of the threads as below everywhere it's needed:在需要的地方检查线程的完成情况,如下所示:

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.
}

The problem with:问题在于:

for(i = 0; i < threads.length; i++)
  threads[i].join();

...is, that threads[i + 1] never can join before threads[i] . ...是, threads[i + 1]永远不能在threads[i]之前加入。 Except the "latch"ed ones, all solutions have this lack.除了“闩锁”的那些,所有的解决方案都有这个缺陷。

No one here (yet) mentioned ExecutorCompletionService , it allows to join threads/tasks according to their completion order:这里(还)没有人提到ExecutorCompletionService ,它允许根据完成顺序加入线程/任务:

public class ExecutorCompletionService<V> extends Object implements CompletionService<V> public class ExecutorCompletionService<V> extends Object implements CompletionService<V>

A CompletionService that uses a supplied Executor to execute tasks. CompletionService使用提供的Executor来执行任务。 This class arranges that submitted tasks are, upon completion, placed on a queue accessible using take.此类安排提交的任务在完成后放置在使用 take 可访问的队列中。 The class is lightweight enough to be suitable for transient use when processing groups of tasks.该类足够轻量级,适合在处理任务组时临时使用。

Usage Examples.用法示例。

Suppose you have a set of solvers for a certain problem, each returning a value of some type Result, and would like to run them concurrently, processing the results of each of them that return a non-null value, in some method use(Result r) .假设您有一组针对某个问题的求解器,每个求解器都返回某种 Result 类型的值,并且希望并发运行它们,处理它们中每个返回非空值的结果,在某些方法中use(Result r) You could write this as:你可以这样写:

 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); } }

Suppose instead that you would like to use the first non-null result of the set of tasks, ignoring any that encounter exceptions, and cancelling all other tasks when the first one is ready:假设您想使用任务集的第一个非空结果,忽略任何遇到异常的结果,并在第一个任务准备好时取消所有其他任务:

 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); }

Since: 1.5 (!)从:1.5 (!)

Assuming use(r) (of Example 1) also asynchronous, we had a big advantage.假设use(r) (例 1 的)也是异步的,我们有很大的优势。 # #

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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