CountDownLatch介绍
- CountDownLatch是一个同步协助类,允许一个或多个线程等待,直到其他线程完成操作。
- CountDownLatch是一种公平锁的实现
- CountDownLatch使用给定的计数值(count)初始化。await方法会阻塞直到当前的计数值(count)由于countDown方法的调用达到0,count为0之后所有等待的线程都会被释放,并且随后对await方法的调用都会立即返回

CountDownLatch应用场景
CountDownLatch一般用作多线程倒计时计数器,强制它们等待其他一组(CountDownLatch的初始化决定)任务执行完成。
场景1 让多个线程等待:模拟并发,让并发线程一起执行
CountDownLatch countDownLatch = new CountDownLatch(1);for (int i = 0; i < 5; i++) {new Thread(() -> {try {//准备完毕……运动员都阻塞在这,等待号令countDownLatch.await();String parter = "【" + Thread.currentThread().getName() + "】";System.out.println(parter + "开始执行……");} catch (InterruptedException e) {e.printStackTrace();}}).start();}Thread.sleep(2000);// 裁判准备发令countDownLatch.countDown();// 发令枪:执行发令
await执行流程:
- 每个线程执行await方法,实际调用acquireSharedInterruptibly方法,arg=1

- CountDownLath的tryAcquireShared的实现是:当state不为0,都是返回-1,也就是有资源的时候都会调用doAcquireSharedInterruptibly

- 而doAcquireSharedInterruptibly,也就是和AQS的方法一样,初始化等待队列,入队,然后把节点的waitState设置为-1,也就是next节点可以被唤醒。
countDown流程:
- 实际调用releaseShared,arg=1

- 在tryReleaseShared里面,会通过cas把state的值-1,只有state-1后的值是0才会返回true。如果最后的state=0,也就是所有的线程都准备好了,然后执行doReleaseShared。

- 此时的head节点的waitState是-1,所以会进入到unparkSuccessor去唤醒线程
场景2 让单个线程等待:多个线程(任务)完成后,进行汇总合并
CountDownLatch countDownLatch = new CountDownLatch(5);for (int i = 0; i < 5; i++) {final int index = i;new Thread(() -> {try {Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(1000));System.out.println(Thread.currentThread().getName()+" finish task" + index );countDownLatch.countDown();} catch (InterruptedException e) {e.printStackTrace();}}).start();}// 主线程在阻塞,当计数器==0,就唤醒主线程往下执行。countDownLatch.await();System.out.println("主线程:在所有任务运行完成后,进行结果汇总");
CountDownLatch实现原理
- 底层基于 AbstractQueuedSynchronizer 实现,CountDownLatch 构造函数中指定的count直接赋给AQS的state;每次countDown()则都是release(1)减1,最后减到0时unpark阻塞线程;这一步是由最后一个执行countdown方法的线程执行的。
- 而调用await()方法时,当前线程就会判断state属性是否为0,如果为0,则继续往下执行,如果不为0,则使当前线程进入等待状态,直到某个线程将state属性置为0,其就会唤醒在await()方法中等待的线程。
CountDownLatch与Thread.join的区别
- CountDownLatch可以手动控制在n个线程里调用n次countDown()方法使计数器进行减一操作,也可以在一个线程里调用n次执行减一操作
- 而 join() 的实现原理是不停检查join线程是否存活,如果 join 线程存活则让当前线程永远等待
