有人能帮助我理解什么是Java CountDownLatch以及什么时候使用它吗?

我不太清楚这个项目是怎么运作的。据我所知,这三个线程同时开始,每个线程将在3000ms后调用CountDownLatch。所以倒数会一个一个递减。锁存变为零后,程序打印“完成”。也许我理解的方式是错误的。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

class Processor implements Runnable {
    private CountDownLatch latch;

    public Processor(CountDownLatch latch) {
        this.latch = latch;
    }

    public void run() {
        System.out.println("Started.");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        latch.countDown();
    }
}

// -----------------------------------------------------

public class App {

    public static void main(String[] args) {

        CountDownLatch latch = new CountDownLatch(3); // coundown from 3 to 0

        ExecutorService executor = Executors.newFixedThreadPool(3); // 3 Threads in pool

        for(int i=0; i < 3; i++) {
            executor.submit(new Processor(latch)); // ref to latch. each time call new Processes latch will count down by 1
        }

        try {
            latch.await();  // wait until latch counted down to 0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("Completed.");
    }

}

当前回答

这个来自Java Doc的例子帮助我清楚地理解了这些概念:

class Driver { // ...
  void main() throws InterruptedException {
    CountDownLatch startSignal = new CountDownLatch(1);
    CountDownLatch doneSignal = new CountDownLatch(N);

    for (int i = 0; i < N; ++i) // create and start threads
      new Thread(new Worker(startSignal, doneSignal)).start();

    doSomethingElse();            // don't let run yet
    startSignal.countDown();      // let all threads proceed
    doSomethingElse();
    doneSignal.await();           // wait for all to finish
  }
}

class Worker implements Runnable {
  private final CountDownLatch startSignal;
  private final CountDownLatch doneSignal;
  Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
     this.startSignal = startSignal;
     this.doneSignal = doneSignal;
  }
  public void run() {
     try {
       startSignal.await();
       doWork();
       doneSignal.countDown();
     } catch (InterruptedException ex) {} // return;
  }

  void doWork() { ... }
}

视觉解释:

显然,CountDownLatch允许一个线程(这里是Driver)等待一堆正在运行的线程(这里是Worker)完成它们的执行。

其他回答

CoundDownLatch使您能够让一个线程等待所有其他线程执行完毕。

伪代码可以是:

// Main thread starts
// Create CountDownLatch for N threads
// Create and start N threads
// Main thread waits on latch
// N threads completes there tasks are returns
// Main thread resume execution

是的,你理解对了。 CountDownLatch的工作原理是闩锁,主线程将等待门被打开。一个线程等待n个线程,这是在创建CountDownLatch时指定的。

任何调用CountDownLatch.await()的线程(通常是应用程序的主线程)都将等待,直到count达到零或被另一个线程中断。所有其他线程都需要在完成或准备就绪后调用CountDownLatch.countDown()进行倒计时。

只要count达到0,等待线程就会继续。CountDownLatch的一个缺点/优点是它不可重用:一旦count达到0,就不能再使用CountDownLatch了。

编辑:

当一个线程(如主线程)需要等待一个或多个线程完成后才能继续处理时,使用CountDownLatch。

在Java中使用CountDownLatch的一个经典示例是一个服务器端核心Java应用程序,该应用程序使用服务体系结构,其中多个服务由多个线程提供,在所有服务成功启动之前应用程序不能开始处理。

注: OP的问题有一个非常简单的例子,所以我没有包括一个。

最好的选择是CyclicBarrier,如https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html 看到的:

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

CountDownLatch在Java中是一种同步器,它允许一个线程在开始处理之前等待一个或多个线程。

CountDownLatch工作在闩锁原理,线程将等待直到门打开。在创建CountDownLatch时,一个线程等待指定的n个线程。

例如:final CountDownLatch = new CountDownLatch(3);

这里我们将计数器设置为3。

任何调用CountDownLatch.await()的线程(通常是应用程序的主线程)都将等待,直到count为零或被另一个线程中断。所有其他线程都需要在完成或准备工作后调用CountDownLatch.countDown()来进行倒计时。一旦count达到0,等待的线程就开始运行。

这里的计数是通过CountDownLatch.countDown()方法递减的。

调用await()方法的线程将一直等待,直到初始计数为零。

为了使计数为零,其他线程需要调用countDown()方法。 一旦计数为零,调用await()方法的线程将恢复(开始执行)。

CountDownLatch的缺点是它不可重用:一旦计数为零,它就不再可用。

在此链接中解释了countDownLatch的最佳实时示例