跳到正文
Elaine Blog
返回

Semaphore、CountDownLatch 与 CyclicBarrier

并发编程

三个协调器解决不同问题:Semaphore 限制同时使用资源的任务数,CountDownLatch 等待一组事件完成,CyclicBarrier 让 固定参与者在阶段边界互相等待。选错工具会让计数含义和失败路径难以维护。

Table of contents

Open Table of contents

运行组合实验

下载协调器样例下载, 运行:

java -cp target/classes dev.elaine.concurrency.SynchronizerSample

程序让三个任务通过 Barrier 同时开始,只允许两个任务同时进入受限区,并由 Latch 等待全部完成。输出顺序可以变化, 但必须包含“三项任务同时开始”和“全部完成”。

Semaphore 表示资源许可

new Semaphore(20) 表示最多 20 个并发许可,适合保护数据库连接、外部 API 并发或稀缺设备。获取后必须在 finally 释放:

permits.acquire();
try {
    callRemoteService();
} finally {
    permits.release();
}

许可数量不是请求速率。20 个许可限制同时进行 20 个调用,不能保证每秒只有 20 个请求。速率限制需要令牌桶等时间模型。

CountDownLatch 表示一次性事件

Latch 初始计数只能递减,达到零后永久开放,不能重置。它适合“等待三个依赖就绪”或“同时启动一批测试”。每个工作任务 都应在 finally 调用 countDown(),否则异常会让等待方永久阻塞。

try {
    loadPart();
} finally {
    finished.countDown();
}

调用 await(timeout, unit) 并处理返回的 false,不要让请求线程无限等待。

CyclicBarrier 表示重复阶段

Barrier 要求固定数量参与者都调用 await(),最后到达者触发 Barrier Action,然后所有线程进入下一阶段。它可重复使用, 适合分阶段并行计算。

一个参与者被中断、超时或失败会破坏 Barrier,其他等待者收到 BrokenBarrierException。调用方必须把“全组失败”作为协议 处理,而不是只重试单个线程。参与者数量会动态变化时,使用 Phaser 更合适。

选择表

需求问题选择
同时最多 N 个任务访问资源Semaphore
一个线程等待 N 个独立事件CountDownLatch
N 个固定线程每轮互相等待CyclicBarrier
参与者动态注册且多阶段Phaser

下一步

阅读并发 Map、List 和 Set 的实现权衡,选择能表达一致性与写入模型的容器。


分享这篇文章:

上一篇
AQS 与 ReentrantLock 源码主线
下一篇
并发 Map、List 和 Set 的实现权衡