如何等待多个线程完成?

时间:2009-08-09 20:26:02

标签: java multithreading parallel-processing wait

简单地等待所有线程进程完成的方法是什么?例如,假设我有:

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()方法都退出?谢谢!

14 个答案:

答案 0 :(得分:153)

您将所有线程放在一个数组中,然后启动所有线程,然后进行循环

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

每个连接都将阻塞,直到相应的线程完成。线程可以以与加入它们不同的顺序完成,但这不是问题:当循环退出时,所有线程都完成。

答案 1 :(得分:37)

一种方法是创建List Thread,创建并启动每个线程,同时将其添加到列表中。一旦启动所有内容,循环回列表并在每个列表上调用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 concurrency utilities的所有新东西)非常灵活,上面的例子几乎没有表面上的。

答案 2 :(得分:24)

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

答案 3 :(得分:8)

完全避免使用Thread类,而是使用java.util.concurrent中提供的更高抽象

ExecutorService类提供的method invokeAll似乎可以满足您的需求。

答案 4 :(得分:8)

而不是join(),这是一个旧API,您可以使用CountDownLatch。我已经修改了您的代码,以满足您的要求。

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

<强>解释

  1. CountDownLatch已根据您的要求使用给定的数量1000进行初始化。

  2. 每个工作线程DoSomethingInAThread将减少已在构造函数中传递的CountDownLatch

  3. 主线程CountDownLatchDemo await(),直到计数变为零。一旦计数变为零,您将获得输出线以下。

    In Main thread after completion of 1000 threads
    
  4. 来自oracle文档页面的更多信息

    public void await()
               throws InterruptedException
    
      

    导致当前线程等到锁存器倒计数到零,除非线程被中断。

    有关其他选项,请参阅相关的SE问题:

    wait until all threads finish their work in java

答案 5 :(得分:5)

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

}

答案 6 :(得分:4)

考虑使用java.util.concurrent.CountDownLatchjavadocs

中的示例

答案 7 :(得分:3)

如果你创建一个线程列表,你可以循环遍历它们和.join()对每个线程,你的循环将在所有线程完成时完成。我没试过。

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

答案 8 :(得分:3)

根据您的需要,您可能还想查看java.util.concurrent包中的CountDownLatch和CyclicBarrier类。如果您希望线程彼此等待,或者您希望对线程执行方式进行更细粒度的控制(例如,在其内部执行中等待另一个线程设置某个状态),它们可能很有用。您还可以使用CountDownLatch来指示所有线程同时启动,而不是在迭代循环时逐个启动它们。标准API文档有一个这样的例子,加上另一个CountDownLatch等待所有线程完成它们的执行。

答案 9 :(得分:2)

这是一个评论,但我还不能做评论。

Martin K ,我很好奇你如何使用ThreadGroup。你以前这样做过吗?

我看到上述情况,建议您查看activeCount - 将 Martin v Löwis关注投票放在一边那一刻,我对activeCount本身有另一种担忧。

警告:我没有尝试使用它,所以我不是这方面的专家,但根据javadocs,它返回活动线程数的估计

就个人而言,我不愿意尝试建立一个估计系统。你有另外想过怎么做,还是我误读了javadoc?

答案 10 :(得分:1)

在第一个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();

答案 11 :(得分:0)

您可以使用对象"ThreadGroup" and its parameter activeCount

执行此操作

答案 12 :(得分:0)

作为 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命令,该命令在每个障碍点运行一次,在聚会中的最后一个线程到达之后,但在释放任何线程之前。在任何一方继续之前,此屏障操作对于更新共享状态非常有用。

答案 13 :(得分:0)

MainForm对我没有帮助。在Kotlin中查看此示例:

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

现在让我将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 时:

  1. 线程按顺序运行。
  2. 第一个样本需要765毫秒,而第二个样本需要3833毫秒。

我们防止阻塞其他线程的解决方案是创建ArrayList:

join

现在,当我们要启动一个新线程时,我们最多将其添加到ArrayList中:

val threads = ArrayList<Thread>()

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
}