有人能帮助我理解什么是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.");
    }

}

当前回答

As mentioned in JavaDoc (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html), CountDownLatch is a synchronization aid, introduced in Java 5. Here the synchronization does not mean restricting access to a critical section. But rather sequencing actions of different threads. The type of synchronization achieved through CountDownLatch is similar to that of Join. Assume that there is a thread "M" which needs to wait for other worker threads "T1", "T2", "T3" to complete its tasks Prior to Java 1.5, the way this can be done is, M running the following code

    T1.join();
    T2.join();
    T3.join();

上面的代码确保线程M在T1、T2、T3完成它的工作之后恢复它的工作。T1, T2, T3可以按任意顺序完成它们的工作。 同样可以通过CountDownLatch实现,其中T1、T2、T3和线程M共享相同的CountDownLatch对象。 "M"请求:countDownLatch.await(); 其中“T1”,“T2”,“T3”是countDownLatch.countdown();

连接方法的一个缺点是M必须知道T1、T2、T3。如果后来添加了一个新的工作线程T4,那么M也必须知道它。使用CountDownLatch可以避免这种情况。 实现后的动作顺序为[T1,T2,T3](T1,T2,T3的顺序可以任意)-> [M]

其他回答

package practice;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch c= new CountDownLatch(3);  // need to decrements the count (3) to zero by calling countDown() method so that main thread will wake up after calling await() method 
        Task t = new Task(c);
        Task t1 = new Task(c);
        Task t2 = new Task(c);
        t.start();
        t1.start();
        t2.start();
        c.await(); // when count becomes zero main thread will wake up 
        System.out.println("This will print after count down latch count become zero");
    }
}

class Task extends Thread{
    CountDownLatch c;

    public Task(CountDownLatch c) {
        this.c = c;
    }

    @Override
    public void run() {
        try {
            System.out.println(Thread.currentThread().getName());
            Thread.sleep(1000);
            c.countDown();   // each thread decrement the count by one 
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

来自oracle关于CountDownLatch的文档:

一种同步辅助工具,允许一个或多个线程等待在其他线程中执行的一组操作完成。

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

CountDownLatch是一种通用的同步工具,可用于许多目的。

初始化为1的CountDownLatch用作简单的开/关锁存器或门:所有调用await的线程都在门处等待,直到由调用countDown()的线程打开。

初始化为N的CountDownLatch可用于使一个线程等待到N个线程完成某个操作,或者某个操作已完成N次。

public void await()
           throws InterruptedException

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

如果当前计数为零,则此方法立即返回。

public void countDown()

减少锁存器的计数,如果计数为零,则释放所有等待线程。

如果当前计数大于零,则递减。如果新的计数为零,则所有等待的线程都将重新启用以进行线程调度。

解释你的例子。

You have set count as 3 for latch variable CountDownLatch latch = new CountDownLatch(3); You have passed this shared latch to Worker thread : Processor Three Runnable instances of Processor have been submitted to ExecutorService executor Main thread ( App ) is waiting for count to become zero with below statement latch.await(); Processor thread sleeps for 3 seconds and then it decrements count value with latch.countDown() First Process instance will change latch count as 2 after it's completion due to latch.countDown(). Second Process instance will change latch count as 1 after it's completion due to latch.countDown(). Third Process instance will change latch count as 0 after it's completion due to latch.countDown(). Zero count on latch causes main thread App to come out from await App program prints this output now : Completed

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

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

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

编辑:

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

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

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

As mentioned in JavaDoc (https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html), CountDownLatch is a synchronization aid, introduced in Java 5. Here the synchronization does not mean restricting access to a critical section. But rather sequencing actions of different threads. The type of synchronization achieved through CountDownLatch is similar to that of Join. Assume that there is a thread "M" which needs to wait for other worker threads "T1", "T2", "T3" to complete its tasks Prior to Java 1.5, the way this can be done is, M running the following code

    T1.join();
    T2.join();
    T3.join();

上面的代码确保线程M在T1、T2、T3完成它的工作之后恢复它的工作。T1, T2, T3可以按任意顺序完成它们的工作。 同样可以通过CountDownLatch实现,其中T1、T2、T3和线程M共享相同的CountDownLatch对象。 "M"请求:countDownLatch.await(); 其中“T1”,“T2”,“T3”是countDownLatch.countdown();

连接方法的一个缺点是M必须知道T1、T2、T3。如果后来添加了一个新的工作线程T4,那么M也必须知道它。使用CountDownLatch可以避免这种情况。 实现后的动作顺序为[T1,T2,T3](T1,T2,T3的顺序可以任意)-> [M]

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