0%

CyclicBarrier和CountDownLatch

CyclicBarrier和CountDownLatch

这两个类都在jdk的并发包中,都可以用来表示代码运行到某个点上

两者的区别

  • CyclicBarrier表示达到一定数量的线程才会运行;CountDownLatch每来一个线程进行减一操作,直到0为止
  • CyclicBarrier只能唤起一个任务;CountDownLatch可以唤起多个任务
  • CyclicBarrier可重用;CountDownLatch不可重用,只能触发一次事件,值为0后就不可再用了
  • CyclicBarrier允许N个线程相互等待;CountDownLatch是允许1或N个线程等待其他线程完成执行

核心区别对比表

特性 CyclicBarrier CountDownLatch
计数器机制 初始值为 parties,每次 await() 减 1,归 0 后自动重置 初始值为 count,每次 countDown() 减 1,归 0 后不可用
线程协作模式 N 个线程互相等待,全部到达屏障后继续执行 1 个(或多个)线程等待其他线程完成操作
重用性 支持循环使用(通过 reset() 或自动重置) 一次性使用,计数器归 0 后无法重置
屏障动作 支持 Runnable 任务,在所有线程到达后执行 无特殊动作,仅唤醒等待线程
典型场景 并行计算的多阶段同步(如 MapReduce) 主线程等待多个子线程完成初始化

代码示例对比

CyclicBarrier:多线程互相等待

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public class CyclicBarrierExample {
public static void main(String[] args) {
int parties = 3;
CyclicBarrier barrier = new CyclicBarrier(parties,
() -> System.out.println("所有线程到达屏障,开始下一步操作"));

for (int i = 0; i < parties; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 准备就绪");
barrier.await(); // 等待其他线程
System.out.println(Thread.currentThread().getName() + " 继续执行");
} catch (Exception e) {
e.printStackTrace();
}
}, "Thread-" + i).start();
}
}
}

// 输出示例:
// Thread-0 准备就绪
// Thread-1 准备就绪
// Thread-2 准备就绪
// 所有线程到达屏障,开始下一步操作
// Thread-2 继续执行
// Thread-1 继续执行
// Thread-0 继续执行

CountDownLatch:主线程等待子线程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int count = 3;
CountDownLatch latch = new CountDownLatch(count);

for (int i = 0; i < count; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 开始工作");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + " 完成工作");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 工作完成,计数器减1
}
}, "Worker-" + i).start();
}

System.out.println("主线程等待所有工作线程完成...");
latch.await(); // 阻塞主线程,直到计数器为0
System.out.println("主线程继续执行");
}
}

// 输出示例:
// Worker-0 开始工作
// Worker-1 开始工作
// Worker-2 开始工作
// 主线程等待所有工作线程完成...
// (约1秒后)
// Worker-0 完成工作
// Worker-1 完成工作
// Worker-2 完成工作
// 主线程继续执行

深入理解:应用场景选择

CyclicBarrier 适用场景

  • 并行计算多阶段任务:如多线程计算矩阵乘法,每个线程负责一行 / 列,全部计算完成后汇总结果。
  • 游戏 / 模拟启动:多个玩家线程必须全部准备好后,游戏才能开始。

CountDownLatch 适用场景

  • 服务启动依赖:主服务等待所有子服务(如数据库、网络、缓存)初始化完成后再启动。
  • 并发性能测试:主线程等待所有工作线程就绪后,同时触发请求以测试并发性能。

总结

  • 选 CyclicBarrier:当需要多个线程反复在某个点同步,且希望自动触发后续操作时。
  • 选 CountDownLatch:当只需要一次性等待其他线程完成操作,且不需要自动触发任务时。

欢迎关注我的其它发布渠道

表情 | 预览
快来做第一个评论的人吧~
Powered By Valine
v1.3.10