本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!前端
放上面这张图有两个意思java
第一 预祝我国奥运健儿在东京奥运会上搏击长空,再创佳绩,为国争光!面试
第二 跟本节内容有关,上面这幅图更有利于你理解本节内容。数据库
CountDownLatch,它是 JDK 提供的并发流程控制的工具类,它是在 java.util.concurrent 包下,在 JDK1.5 之后加入的。后端
Count - Down - Latchmarkdown
计数,关闭/向下,门阀 (英文很差,勿喷)并发
那就能够理解为,一道门槛,按照计数量一个一个安排进出。哈哈 这是我我的理解,下面举个正常的例子,让你恍然大悟。dom
这幅图就很好理解了。ide
能够看到,最开始 CountDownLatch 设置的初始值为 3,而后 T0 线程上来就调用 await 方法,它的做用是让这个线程开始等待,等待后面的 T一、T二、T3,它们每一次调用 countDown 方法,3 这个数值就会减 1,也就是从 3 减到 2,从 2 减到 1,从 1 减到 0,一旦减到 0 以后,这个 T0 就至关于达到了本身触发继续运行的条件,因而它就恢复运行了。函数
这个例子正好与第一个例子相反,第一个是等全部人到了,我才开始。这个例子是等全部运动员跑到终点以后,裁判才会宣布中止。CountDownLatch就是裁判!
下面介绍一下 CountDownLatch 的主要方法。
它的构造函数是传入一个参数,该参数 count 是须要倒数的数值。
await():调用 await() 方法的线程开始等待,直到倒数结束,也就是 count 值为 0 的时候才会继续执行。
await(long timeout, TimeUnit unit):await() 有一个重载的方法,里面会传入超时参数,这个方法的做用和 await() 相似,可是这里能够设置超时时间,若是超时就再也不等待了。
countDown():把数值倒数 1,也就是将 count 值减 1,直到减为 0 时,以前等待的线程会被唤起。
在实际开发场景中,不少状况下须要咱们初始化一系列的前置操做,好比数据库先创建链接,全部bean都加载完毕,在这些准备条件都完成以前,是不能进行下一步工做的,因此这就是利用 CountDownLatch 的一个很好场景,咱们可让应用程序的主线程在其余线程都准备完毕以后再继续执行。
咱们还拿运动员跑步这个场景用代码模拟下。好比在比赛跑步时有 5 个运动员参赛,终点有一个裁判员,何时比赛结束呢?那就是当全部人都跑到终点以后,这至关于裁判员等待 5 个运动员都跑到终点,宣布比赛结束!
public class RunDemo1 {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println(no + "号运动员完成了比赛。");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}
};
service.submit(runnable);
}
System.out.println("等待5个运动员都跑完.....");
latch.await();
System.out.println("全部人都跑完了,比赛结束。");
}
}
复制代码
在这段代码中,咱们新建了一个初始值为 5 的 CountDownLatch,而后创建了一个固定 5 线程的线程池,用一个 for 循环往这个线程池中提交 5 个任务(5个运动员),每一个任务表明一个运动员,这个运动员会首先随机等待一段时间,表明他在跑步,而后打印出他完成了比赛,在跑完了以后,一样会调用 countDown 方法来把计数减 1。
以后咱们再回到主线程,主线程打印完“等待 5 个运动员都跑完”这句话后,会调用 await() 方法,表明让主线程开始等待,在等待以前的那几个子线程都执行完毕后,它才会认为全部人都跑完了比赛。这段程序的运行结果以下所示:
等待5个运动员都跑完.....
4号运动员完成了比赛。
3号运动员完成了比赛。
1号运动员完成了比赛。
5号运动员完成了比赛。
2号运动员完成了比赛。
全部人都跑完了,比赛结束。
复制代码
能够看出,直到 5 个运动员都完成了比赛以后,主线程才会继续,并且因为子线程等待的时间是随机的,因此各个运动员完成比赛的次序也是随机的。
这和第一个用法有点相反,咱们再列举一个实际的场景,好比在运动会上,刚才说的是裁判员等运动员,如今是运动员等裁判员。在运动员起跑以前都会等待裁判员发号施令,一声令下运动员统一块儿跑,咱们用代码把这件事情描述出来,以下所示:
public class RunDemo2 {
public static void main(String[] args) throws InterruptedException {
System.out.println("运动员有5秒的准备时间");
CountDownLatch countDownLatch = new CountDownLatch(1);
ExecutorService service = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
final int no = i + 1;
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(no + "号运动员准备完毕,等待裁判员的发令枪");
try {
countDownLatch.await();
System.out.println(no + "号运动员开始跑步了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.submit(runnable);
}
Thread.sleep(5000);
System.out.println("5秒准备时间已过,发令枪响,比赛开始!");
countDownLatch.countDown();
}
}
复制代码
在这段代码中,首先打印出了运动员有 5 秒的准备时间,而后新建了一个 CountDownLatch,其倒数值只有 1;接着,一样是一个 5 线程的线程池,而且用 for 循环的方式往里提交 5 个任务,而这 5 个任务在一开始时就让它调用 await() 方法开始等待。
接下来咱们再回到主线程。主线程会首先等待 5 秒钟,这意味着裁判员正在作准备工做,好比他会喊“各就各位,预备”这样的话语;而后 5 秒以后,主线程会打印出“5 秒钟准备时间已过,发令枪响,比赛开始”的信号,紧接着会调用 countDown 方法,一旦主线程调用了该方法,那么以前那 5 个已经调用了 await() 方法的线程都会被唤醒,因此这段程序的运行结果以下:
运动员有5秒的准备时间
2号运动员准备完毕,等待裁判员的发令枪
1号运动员准备完毕,等待裁判员的发令枪
3号运动员准备完毕,等待裁判员的发令枪
4号运动员准备完毕,等待裁判员的发令枪
5号运动员准备完毕,等待裁判员的发令枪
5秒准备时间已过,发令枪响,比赛开始!
2号运动员开始跑步了
1号运动员开始跑步了
5号运动员开始跑步了
4号运动员开始跑步了
3号运动员开始跑步了
复制代码
能够看到,运动员首先会有 5 秒钟的准备时间,而后 5 个运动员分别都准备完毕了,等待发令枪响,紧接着 5 秒以后,发令枪响,比赛开始,因而 5 个子线程几乎同时开始跑步了。
刚才讲了两种用法,其实这两种用法并非孤立的,甚至能够把这两种用法结合起来,好比利用两个 CountDownLatch,第一个初始值为多个,第二个初始值为 1,这样就能够应对更复杂的业务场景了;
CountDownLatch 类在建立实例的时候,须要在构造函数中传入倒数次数,而后由须要等待的线程去调用 await 方法开始等待,而每一次其余线程调用了 countDown 方法以后,计数便会减 1,直到减为 0 时,以前等待的线程便会继续运行。
小贴士: 咱们在实际开发中还能够在统计数据中应用,好比咱们的数据展板统计,后台可能有若干个查询统计,那就用此思想,将每一个耗时查询放到线程池里,一块儿跑,让该接口快速响应。
这是我在掘金的第七篇文章了。感谢阅读,并但愿之后持续关注,我会输出更多技术干货,咱们共同进步!
之后可能会分为几大专题,相似于并发专题,源码专题,面试专题等(只会分享干货)。
感兴趣的能够点击我头像查看历史文章,每一篇都是干货哦!
点滴积累,点滴分享,咱们共同成长!