Skip to main content

倒计时器CountDownLatch

huhxAbout 3 minjavaConcurrency-ToolkitConcurrency

CountDownLatch是JDK 5+里面闭锁的一个实现,允许一个或者多个线程等待某个事件的发生。今天我们通过一些实例来学习一下它的用法。

CountDownLatch的使用

CountDownLatch被用来同步一个或多个线程,强制它们等待由其他线程执行的一组操作完成。你可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用await()方法都将阻塞,直至这个计数值达到0。其他线程在结束工作时,可以在该对象上调用countDown()方法来减小这个计数值。

CountDownLatch被设计为只触发一次,计数值不能被重置。如果你需要能够重置计数值的版本,则可以使用CyclicBarrier

count-down-latch
count-down-latch

只有等线程1、线程2和线程3中都运行结束后,才可以运行线程4。即一定数量的线程都完成工作后,才可以共同触发后续的一个或多个线程的开始工作。

CountDownLatch的构建函数接收一个整数为参数,即当前这个计数器的计数个数。

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
}

其中的count其实就是: 线程可以通过等待之前必须调用countDown的次数。

CountDownLatch中的方法:

public void await() throws InterruptedException
public boolean await(long timeout, TimeUnit unit) throws InterruptedException
public void countDown()
public long getCount()
  • await: 导致当前线程等待,直到锁存器倒数为零或者线程被中断。带有参数的await方法多一种方式结束等待:指定的等待时间已过
  • countDown: 递减锁存器的计数,如果计数达到零,则释放所有等待线程
  • getCount: 返回当前计数,一般用于调度或者测试

下面我们给出一个例子:阻止任何工人开始工作,直到司机准备好

public class CountDownLatchTest {
    final static CountDownLatch startSignal = new CountDownLatch(1);
    final static CountDownLatch doneSignal = new CountDownLatch(5);

    public static void main(String[] args) throws Exception {
        for (int i = 0; i < 5; ++i)
            new Thread(new TaskWorker()).start();

        startSignal.countDown();
        System.out.println("Diver is ready, workers can start to work: " + LocalDateTime.now());

        TimeUnit.SECONDS.sleep(1);
        System.out.println("Diver is waiting for the workers done their works " + LocalDateTime.now());

        doneSignal.await();
        System.out.println("Workers has done their works, Diver start the car. " + LocalDateTime.now());
    }

    private static class TaskWorker implements Runnable {
        @Override
        public void run() {
            try {
                startSignal.await();
                System.out.println("Worker: " + Thread.currentThread().getName() + " done the work, time: " + LocalDateTime.now());
                doneSignal.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}








 





 







 

 






输出结果为:

Diver is ready, workers can start to work: 2023-08-15T20:40:59.137129
Worker: Thread-0 done the work, time: 2023-08-15T20:40:59.137085
Worker: Thread-1 done the work, time: 2023-08-15T20:40:59.137092
Worker: Thread-3 done the work, time: 2023-08-15T20:40:59.137106
Worker: Thread-2 done the work, time: 2023-08-15T20:40:59.137088
Worker: Thread-4 done the work, time: 2023-08-15T20:40:59.137113
Diver is waiting for the workers done their works 2023-08-15T20:41:00.144588
Workers has done their works, Diver starts the car. 2023-08-15T20:41:00.145089

这里我们创建两个CountDownLatch

  • startSignal的计数量为1,可以当成是启动信号,阻止任何工人开始工作,直到驾驶员准备好让他们继续操作
  • doneSignal的计数量为5,可以当成是完成信号,让驾驶员等待直到所有工作人员完成任务

代码里面我们创建五个线程TaskWorker模拟五个工人,他们需要一直等待startSignal.await()直到驾驶员发送开始的信号。在main线程中startSignal.countDown()表示驾驶员发送了开始的信号。于是五个工人开始了工作(done the work...),于此同时驾驶员在等待所有工人完成工作doneSignal.await()

工人完成自己的工作时,会告知驾驶员自己工作已完成的信号doneSignal.countDown()。当驾驶员收到所有工人完成工作的信号后,便开始启动了车辆扬长而去(Diver start the car)....

CountDownLatch的分析

FAQ

总结

参考