CountDownLatch

138 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

一、简介

CountDownLatch是从JDK1.5开始提供的一个辅助并发编程的一个类,它位于在JUC包中。

允许一个或多个线程等待,直到在其他线程中执行的一组操作完成。

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

二、使用场景

CountDownLatch是一种多功能同步工具,可用于多种用途。

使用计数 1 初始化的CountDownLatch用作简单的开/关锁存器或门:所有调用await的线程在门处等待,直到它被调用countDown的线程打开。

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

示例用法:这是一对类,其中一组工作线程使用两个倒计时锁存器:

第一个是启动信号,它阻止任何工人继续前进,直到司机准备好让他们继续前进;

第二个是完成信号,允许驱动程序等待所有工作人员完成。

 public class Driver {

   public static void main(String args []) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(5);

        for (int i = 0; i < 5; ++i) {
            // create and start threads
            new Thread(new Worker(startSignal, doneSignal)).start();
        }
       System.out.println("don't let run yet"); // don't let run yet
        startSignal.countDown();      // let all threads proceed
       System.out.println("waiting threads running");
       doneSignal.await();           // wait for all to finish
       System.out.println("all threads run over");
    }



    static 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() throws InterruptedException {
            Thread.sleep(5000);
            System.out.println(Thread.currentThread().getName()+"=========");

        }
    }

}

另一个典型的用法是将一个问题分成 N 个部分,用一个 Runnable 描述每个部分,该 Runnable 执行该部分并在锁存器上倒计时,并将所有 Runnables 排队到一个 Executor。当所有子部分都完成后,协调线程就可以通过await了。 (当线程必须以这种方式重复倒计时时,需要使用CyclicBarrier 。)

 class Driver2 { // ...
   void main() throws InterruptedException {
     CountDownLatch doneSignal = new CountDownLatch(N);
     Executor e = ...

     for (int i = 0; i < N; ++i) // create and start threads
       e.execute(new WorkerRunnable(doneSignal, i));

     doneSignal.await();           // wait for all to finish
   }
 }

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

   void doWork() { ... }
 }

三、常用方法

  • public void await() throws InterruptedException:调用await()方法的线程会被挂起,等待直到count值为0再继续执行。
  • public void countDown(): count值递减1.