核心观点:
- CountdownLatch阻塞主线程,等所有子线程完结了再继续下去。
- Syslicbarrier阻塞一组线程,直至某个状态之后再全部同时执行,并且所有线程都被释放后,还能通过
reset
来重用。
CountDownLatch | CyclicBarrier |
---|---|
减计数方式 | 加计数方式 |
计算为0时释放所有等待的线程 | 计数达到指定值时释放所有等待线程 |
计算为 0 时,无法重置 | 计数达到指定值时,计数置为0重新开始 |
调用 countDown() 方法计数减一,调用 await()方法只进行阻塞,对计数没任何影响 | 调用 await() 方法计数加 1,若加 1 后的值不等于构造方法的值,则线程阻塞 |
不可重复利用 | 可重复利用 |
一、CountDownLatch用法
CountDownLatch类只提供了一个构造器:
public CountDownLatch(int count) { }; //参数count为计数值
然后下面这3个方法是CountDownLatch类中最重要的方法
//调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public void await() throws InterruptedException { };
//和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
//将count值减1
public void countDown() { };
- 可以看到
子线程
并没有因为调用latch.countDown而阻塞,会继续进行该做的工作,只是通知计数器-1,CountdownLatch适用于所有线程通过某一点后通知方法,
使用
- 需要阻塞的线程调用CountDownLatch.wait(),
主线程阻塞,等待计数器为0,执行逻辑
- 子线程调用CountDownLatch.countDown() ,
子线程不会被阻塞,继续执行
应用场景
开始执行前等待n 个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N 个外部系统已经启动和运行了,例如处理excel 中多个表单。
二、CyclicBarrier用法
CyclicBarrier则适合让所有线程在同一点同时执行
,CyclicBarrier提供2个构造器:
public CyclicBarrier(int parties, Runnable barrierAction) {
}
public CyclicBarrier(int parties) {
}
参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。
CyclicBarrier中最重要的方法就是await方法
//挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;
public int await() throws InterruptedException, BrokenBarrierException { };
//让这些线程等待至一定的时间,
//如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
使用
- 子线程调用cyclicBarrier.await();
调用await的线程会被阻塞,等待计数器满后继续执行
- 当计数器满后:自动调用构造器里面的 Runnable (执行其逻辑),后释放阻塞的子线程
应用场景
CyclicBarrier 可以用于多线程计算数据,最后合并计算结果的场景。,比如有3个线程Ta、Tb、Tc在执行某业务逻辑,只有当3个线程都到达屏障时,才开始执行后续的逻辑。
总结
- CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
- CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。