CountDownLatch and CyclicBarrier
CountDownLatch和CyclicBarrier从字面上理解是“向下数的门闩”和“循环的障碍”,觉得怪怪的,但是即便翻译成更加书面语的“闭锁”或“栅栏”也并不容易理解。《Java并发编程实战》中详细讲解了两者的作用,也举了例子,但理解起来印象不深。如果只是从异同的角度去比较两者,网上倒是有不少文章,但是对于编程实践往往不如通俗的例子来得实在。
经过一番比较,发现CSDN博客酷鱼影子写的不错,下面摘录原博客的一些内容来解释这两者的适用场景。摘录内容略有修改,如果有版权问题,请作者告知。
— 摘录内容(例子参考原博文) —
在多线程程序设计中,经常会遇到一个线程等待一个或多个线程的场景,遇到这样的场景应该如何解决?如果是一个线程等待另一个线程,可以通过wait()
和notify()
来实现;如果是一个线程等待多个线程,那么可以使用CountDownLatch和CyclicBarrier来实现比较好的控制。
CountDownLatch
下面来详细描述CountDownLatch的应用场景:例如百米赛跑中8名运动员同时起跑,由于速度的快慢,肯定会出现先到终点和晚到终点的情况,而终点有个统计成绩的仪器,当所有选手到达终点时,它会统计所有人的成绩并进行排序,然后把结果发送到汇报成绩的系统。其实这就是一个CountDownLatch的应用场景:一个线程(终点统计成绩的仪器)或多个线程等待其他线程(8名运动员)运行达到某一目标后进行自己的下一步工作,而被等待的“其他线程”达到这个目标后继续自己下面的任务。
那么,如何来通过CountDownLatch来实现上述场景的线程控制和调度呢?
JDK中CountDownLatch类有一个常用的构造方法:CountDownLatch(int count)
以及两个常用的方法:await()
和countdown()
。其中count是一个计数器中的初始化数字,比如初始化的数字是2,当一个线程里调用了countdown()
,则这个计数器就减一,当线程调用了await()
,则这个线程就等待这个计数器变为0,当这个计数器变为0时,这个线程就继续自己下面的工作。
CyclicBarrier
下面详细描述下CyclicBarrier的应用场景:有四个游戏玩家玩游戏,游戏有三个关卡,每个关卡必须要所有玩家都到达后才能允许通关。其实这个场景里的玩家中如果有玩家A先到了关卡1,他必须等待其他所有玩家都到达关卡1时才能通过,也就是说线程之间需要互相等待,这和CountDownLatch的应用场景有区别,CountDownLatch里的线程是到了运行的目标后继续干自己的其他事情,而这里的线程需要等待其他线程后才能继续完成下面的工作。
JDK中CyclicBarrier类有两个常用的构造方法:
1.CyclicBarrier(int parties)
这里的parties也是一个计数器,例如,初始化时parties里的计数是3,于是拥有该CyclicBarrier对象的线程当parties的计数为3时就唤醒,注:这里parties里的计数在运行时当调用CyclicBarrier:await()
时,计数就加1,一直加到初始的值。
2.CyclicBarrier(int parties, Runnable barrierAction)
这里的parties与上一个构造方法的解释是一样的,这里需要解释的是第二个参数(Runnable barrierAction),这个参数是一个实现Runnable接口的类的对象,也就是说当parties加到初始值时就执行barrierAction的内容。
— 摘录内容结束 —
其实细读一下Javadoc也是可以的,讲得也比较清楚: