1. 什么是CountDownLatch
CountDownLatch是一个线程倒计时锁。
CountDownLatch是通过一个计数器来实现的,计数器的初始值是线程的数量。
每当一个线程执行完毕后,计数器的值就减1,
当计数器的值为0时,表示所有线程都执行完毕,
然后在闭锁上(调用await方法的线程)等待的线程就可以恢复工作了。
2. 应用场景
把一个大任务拆分N个部分,让多个工作线程(Worker)执行。
每个工作线程(Worker)执行完自己的部分计数器就减1。
当所有子部分都完成后,主线程才继续向下执行。
例子代码:
public class TestCountDownLatch1 { public static void main(String[] args) throws InterruptedException { int count = 3; CountDownLatch countDownLatch = new CountDownLatch(count); for (int i = 0; i < count; i++) { final int index = i; new Thread(() -> { try { Thread.sleep(1000 + ThreadLocalRandom.current().nextInt(1000)); System.out.println("finish" + index + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); }finally{ countDownLatch.countDown(); } }).start(); } // 主线程在阻塞,当计数器==0,就唤醒主线程往下执行 countDownLatch.await(); System.out.println("主线程:在所有任务运行完成后,进行结果汇总"); } }
更实际的例子:
我们打开一个电商的个人中心页面。
在该页面, 我们需要调用:
1. 用户信息接口
2. 用户订单接口
3. 用户会员信息等接口
然后合并后一起给到前端。
假设每个接口最长耗时为1s,如果我们同步调用的话最大耗时时间是3s,
如果我们采用异步调用然后合并结果,所以最大的耗时时间是3s。
每个接口调用返回数据后调用countDown方法,让计数器进行减1,
当把计数器减为0时的这个线程会去唤醒主线程,让它继续往下走。
3. CountDownLatch 实现原理
CountDownLatch是通过AQS的state字段来实现的一个计数器。
计数器的初始值(state的值)为new CountDownLatch设置的数量,
每次调用countDown的时候,state的值会进行减1,
最后某个线程将state值减为0时,会把调用了await()进行阻塞等待的线程进行唤醒。
CountDownLatch重写了tryReleaseShared这个方法,
只有当state这个字段被设置为0时,也就是tryReleaseShared返回true的情况就会执行doReleaseShared方法,
把调用了await的线程进行唤醒。
部分源码:
public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; } protected boolean tryReleaseShared(int releases) { // Decrement count; signal when transition to zero for (;;) { int c = getState(); if (c == 0) return false; int nextc = c-1; if (compareAndSetState(c, nextc)) return nextc == 0; } }