当前位置:

CountdownLatch 和 CycliBarriar 有什么区别?

机器助手 |  2025-07-13
 102人浏览

1、CountdownLatch

先来看CountDownLatch,它是一个组合词。CountDown 的意思是 倒计时,Latch 的意思是 门闩 ,也被翻译成发令枪。在JDK 注释中是这样的:

A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes。

翻译过来就是说,让一个或多个线程持续等待,直到其他多线程执行的一组操作全部完成以后,这些等待的线程才会继续执行。

就好比是,有多位选手参加一场百米赛跑,裁判员需要等待全部选手就绪,并且在同一起跑线上。然后,裁判会发出号令:“各就位,预备跑”,随着发令枪响,所有选手才能全部起跑。在这个场景中,各位参赛选手就是线程,而裁判就是CountDownLatch。

我们在实际开发中,有以下两个使用场景可以用CountDownLatch来实现:

(1)让单个线程等待多个线程的场景。

比如,一个服务需要从多个远程接口获取数据,我们可以创建多个线程来分别调用远程接口,等待所有远程接口都获得返回数据之后,主服务线程再往下继续执行。像并发计算,结果汇总等等。

(2)让多个线程等待的场景。

比如,模拟秒杀场景,让一组线程同时等待,同时恢复执行,实现最大程度的并行性。

需要注意的是,当高并发请求时,Countdownlatch的await方法有可能会引起死锁。

如果线程池中线程的数量较少,在高并发时会出现多个请求占用了全部的线程,但是每个请求又需要await()其他线程,被等待的线程拿不到线程资源无法执行,导致多个请求同时进入线程阻塞,最后形成死锁。

当然,我们可以使用自定义线程池来扩大线程数量,并且建立线程池拒绝机制来解决死锁问题。

2、CyclicBarrier

再来看 CyclicBarrier [ˈsaɪklɪk] [ˈbæriə],它也是一个组合词。其中Cyclic 的意思是 循环 ,Barrier 的意思是 屏障 ,又被翻译成栅栏。在JDK 注释中是这样描述的:

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point.

CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other.

The barrier is called cyclic because it can be re-used after the waiting threads are released.

翻译过来总结为以下三点:

  1. CyclicBarrier 是一个同步辅助类,它允许一组线程相互等待直到所有线程都到达一个公共的屏障点。
  2. 在程序中有固定数量的线程,这些线程有时候必须等待彼此,这种情况下,使用 CyclicBarrier 很有帮助。
  3. 这个屏障之所以用循环修饰,是因为在所有的线程释放彼此之后,这个屏障可以重复使用。

从字面上理解的话,它的功能和CountdownLatch非常类似,也是等待所有参加比赛的选手全部就绪以后,才能开始起跑。它是另外一种多线程并发控制工具,和CountdownLatch不同的是,CyclicBarrier可以重复使用。

我们在实际开发中,CyclicBarrier可以用于多线程计算数据,最后合并计算结果的应用场景。

比如,需要计算N组人一年的平均工资,每组需要多个线程并行计算,计算完一组,再开始下一组,这样就需要多轮并行计算。这个场景下,CyclicBarrier 比 CountDownLatch 更适合。

3、两者区别

最后,总结一下CountDownLatch和CyclicBarrier的区别,从以下四个方面来分析:

  1. CountDownLatch的计数器只能使用一次。而CyclicBarrier的计数器可以使用reset() 方法重置。
  2. CyclicBarrier能处理更为复杂的业务场景,比如计算发生错误,可以结束阻塞,重置计数器,重新执行程序
  3. CyclicBarrier提供getNumberWaiting()方法,可以获得CyclicBarrier阻塞的线程数量,还提供isBroken()方法,可以判断阻塞的线程是否被中断,等等。
  4. CountDownLatch会阻塞主线程,CyclicBarrier不会阻塞主线程,只会阻塞子线程。

4、CountDownLatch 用法

CountDownLatch 只提供了一个构造器


 

public CountDownLatch(int count) {             //参数count为计数值 } 

然后下面这3个方法是CountDownLatch类中最重要的方法:


 

public void await() throws InterruptedException {            //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行 }    public boolean await(long timeout, TimeUnit unit) throws InterruptedException {           //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行 } public void countDown() {           //将count值减1 }

CountDownLatch, 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

例子代码


 

import java.util.concurrent.CountDownLatch; public class countDownlatchTest {     public static void main(String[] args) throws InterruptedException {         CountDownLatch countDownLatch = new CountDownLatch(5);         for(int i=0;i<5;i++){             new Thread(new readNum(i,countDownLatch)).start();         }         countDownLatch.await();         System.out.println("线程执行结束。。。。");     }     static class readNum  implements Runnable{         private int id;         private CountDownLatch latch;         public readNum(int id,CountDownLatch latch){             this.id = id;             this.latch = latch;         }         @Override         public void run() {             synchronized (this){                 System.out.println("id:"+id);                 latch.countDown();                 System.out.println("线程组任务"+id+"结束,其他任务继续");             }         }     } }

输出结果:


 

id:1 线程组任务1结束,其他任务继续 id:0 线程组任务0结束,其他任务继续 id:2 线程组任务2结束,其他任务继续 id:3 线程组任务3结束,其他任务继续 id:4 线程组任务4结束,其他任务继续 线程执行结束。。。。

线程在countDown()之后,会继续执行自己的任务,而CyclicBarrier会在所有线程任务结束之后,才会进行后续任务。

5、CyclicBarrier 用法

CyclicBarrier提供2个构造器


 

public CyclicBarrier(int parties, Runnable barrierAction) { } public CyclicBarrier(int parties) { }

参数parties指让多少个线程或者任务等待至barrier状态;参数barrierAction为当这些线程都达到barrier状态时会执行的内容。

CyclicBarrier中最重要的方法就是await方法


 

public int await() throws InterruptedException, BrokenBarrierException {  //挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务; } public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException {  //让这些线程等待至一定的时间,如果还有线程没有到达barrier状态 //就直接让到达barrier的线程执行后续任务 }

例子代码:


 

import java.util.concurrent.CyclicBarrier; public class cyclicBarrierTest {     public static void main(String[] args) throws InterruptedException {         CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {             @Override             public void run() {                 System.out.println("线程组执行结束");             }         });         for (int i = 0; i < 5; i++) {             new Thread(new readNum(i,cyclicBarrier)).start();         }         //CyclicBarrier 可以重复利用,         // 这个是CountDownLatch做不到的 //        for (int i = 11; i < 16; i++) { //            new Thread(new readNum(i,cyclicBarrier)).start(); //        }     }     static class readNum  implements Runnable{         private int id;         private CyclicBarrier cyc;         public readNum(int id,CyclicBarrier cyc){             this.id = id;             this.cyc = cyc;         }         @Override         public void run() {             synchronized (this){                 System.out.println("id:"+id);                 try {                     cyc.await();                     System.out.println("线程组任务" + id + "结束,其他任务继续");                 } catch (Exception e) {                     e.printStackTrace();                 }             }         }     } }

输出结果:


 

id:1 id:2 id:4 id:0 id:3 线程组执行结束 线程组任务3结束,其他任务继续 线程组任务1结束,其他任务继续 线程组任务4结束,其他任务继续 线程组任务0结束,其他任务继续 线程组任务2结束,其他任务继续

文章评论