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

}

当前回答

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

其他回答

来自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

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

One good example of when to use something like this is with Java Simple Serial Connector, accessing serial ports. Typically you'll write something to the port, and asyncronously, on another thread, the device will respond on a SerialPortEventListener. Typically, you'll want to pause after writing to the port to wait for the response. Handling the thread locks for this scenario manually is extremely tricky, but using Countdownlatch is easy. Before you go thinking you can do it another way, be careful about race conditions you never thought of!!

伪代码:

CountDownLatch latch;
void writeData() { 
   latch = new CountDownLatch(1);
   serialPort.writeBytes(sb.toString().getBytes())
   try {
      latch.await(4, TimeUnit.SECONDS);
    } catch (InterruptedException e) {
   }
}
class SerialPortReader implements SerialPortEventListener {
    public void serialEvent(SerialPortEvent event) {
        if(event.isRXCHAR()){//If data is available
            byte buffer[] = serialPort.readBytes(event.getEventValue());
            latch.countDown();
         }
     }
}

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

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

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