java CountDownLatch等待多个并发事件的完成

是什么?为什么?怎么做?

 

一、是什么

CountDownLatch是java.util.concurrent包提供的一个同步辅助类,这个类可以让一条或多条线程等待其他线程完成各自的工作后再执行。

例如:开启线程进行数据操作,主线程希望所有子线程都执行完毕后,再进行汇总。这时候就适合使用CountDownLatch。

这个场景有几个特点:

1.主线程等待所有子线程都执行完毕(让某一条或者多条线程保持等待状态,直到一定数量的其他线程到达某个时间点(在这里是子线程执行完毕的时间点))。

2.主线程再进行汇总(一定数量的线程到达时间点后,等待状态中的线程开始执行)。

二、为什么

之前我写过一篇文章:

java,go 保证所有子线程都执行完毕

提出了两种相当简单的方法,都有一定的问题:

1.让主线程使用sleep方法,睡眠一段长时间,保证别的线程都先执行完成

如果处理上面的情景,需要先估算一段时间,让汇总的主线程休眠。等到子线程都统计完了,等待主线程醒来进行汇总。

如果时间算长了,将浪费等待时间,效率很低。如果时间算短了,不能保证子线程都能先于主线程执行完毕,结果错误。

2.在主线程之前对所有子线程使用join方法

所有子线程都在主线程主线程前使用join方法,难以管理(使用线程组也许能改善这个问题),比较麻烦,也更容易出bug。

如果使用CountDownLatch类,可以更好地管理线程的执行顺序。这就是我们使用CountDownLatch类的原因。

三、怎么做

1.简单例子

这里定义了一个CountDownLatch:

因为我打算等待三条线程执行完成后,才继续执行主线程,所以这里的threadnum为3。

开启完所有子线程后,让主线程进入等待状态:

当子线程中countDownLatch.countDown的次数到达定义的线程数时(这里是3),主线程就结束等待,继续运行。

需要掌握的是await方法和countDown方法的配合:

线程在设置countDownLatch对象时,实际上是启用了一个计数器。因为我们设定了等待3条线程完成,所以计数器的值为3。当一条线程使用countDownLatch.await方法时,该线程将进入等待状态。而每当线程执行完成时,都可以执行countDownLatch.countDown方法,让计数器的值-1。当计数器的值降为0时(因为有3条线程,所以这里的过程是3-1-1-1=0),线程结束等待状态,继续执行。

在这里,因为主线程等待子线程“完成”,所以countDownLatch.countDown方法最好写在finally块中,保证在线程运行完成后才执行。

运行结果:

如果没有使用countDownLatch,大多数情况下主线程中的syso要先于子线程的syso(主线程没有等待子线程执行完毕就擅自执行了)。

2.赛跑例子

上面的简单例子是主线程等待子线程,比较简单。countDownLatch还可以实现相互等待的复杂功能,比如说赛跑的情景:

5个运动员准备赛跑,裁判需要等待他们准备完毕。准备完毕后,运动员等待裁判发令,开始赛跑。

在这个情景中,主线程等待子线程准备完毕,子线程等待主线程发出命令。

运行结果为:

顺带一提,如果某条线程使用了await方法,那么最好保证有足够的线程countDown,否则线程将一直await下去。

为了避免一直等待的状况,await方法可以设定最长等待时间。到了时间,无论是否有足够的线程countDown,线程也会结束await状态,继续执行。

四、总结

一个或者多个任务,需要等待其他的一些任务执行到一定程度后,才开始执行。

这样的情景就比较适合使用countDownLatch。

发表评论

电子邮件地址不会被公开。 必填项已用*标注