Semaphore 是计数信号量,能实现并发数限制类限流,但不支持时间窗口、QPS 控制或按接口维度限流;适合保护本地有限资源,需配对 acquire/release 并用 finally 释放。

Semaphore 是什么,它真能做限流吗
Semaphore 不是专为限流设计的工具,但它能实现“并发数限制”这一核心限流语义。它的本质是计数信号量,acquire() 消耗许可,release() 归还许可,许可总数在构造时固定。只要控制好许可数和 acquire/release 的配对时机,就能达成类似限流的效果。
注意:它不感知时间窗口、不自动清理过期请求、也不区分请求来源——这些是成熟限流器(如 Sentinel、Resilience4j)才提供的能力。用 Semaphore 做限流,只适合简单场景,比如限制同时最多 5 个线程访问某个本地资源。
如何正确初始化和使用 Semaphore 实现并发限流
关键在于构造参数与 acquire/release 的位置。错误地放在方法内部、漏掉 finally 块释放、或在异常路径中未释放,都会导致许可永久泄漏,最终所有线程阻塞在 acquire() 上。
- 使用
new Semaphore(int permits, boolean fair);通常设fair = false(默认),避免排队开销 -
acquire()必须配对release(),且release()应该放在finally块中 - 不要在循环里反复
acquire()而不release(),除非你明确需要多许可(即acquire(3)对应release(3))
private final Semaphore semaphore = new Semaphore(3);
public void doWork() {
try {
semaphore.acquire(); // 阻塞直到拿到许可
// 执行受保护的操作
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
semaphore.release(); // 必须释放,否则许可数会越来越少
}
}
acquire() 会阻塞,有没有非阻塞或带超时的替代方案
直接调用 acquire() 会无限期等待,生产环境几乎不可接受。应优先使用带超时的版本,并根据返回值判断是否获取成功。
PHP网络编程技术详解由浅入深,全面、系统地介绍了PHP开发技术,并提供了大量实例,供读者实战演练。另外,笔者专门为本书录制了相应的配套教学视频,以帮助读者更好地学习本书内容。这些视频和书中的实例源代码一起收录于配书光盘中。本书共分4篇。第1篇是PHP准备篇,介绍了PHP的优势、开发环境及安装;第2篇是PHP基础篇,介绍了PHP中的常量与变量、运算符与表达式、流程控制以及函数;第3篇是进阶篇,介绍
立即学习“Java免费学习笔记(深入)”;
-
tryAcquire():立即返回boolean,不阻塞,适合“尽力而为”场景 -
tryAcquire(long timeout, TimeUnit unit):指定最大等待时间,超时返回false,必须检查返回值 - 超时后不调用
release(),因为根本没拿到许可
if (semaphore.tryAcquire(1, TimeUnit.SECONDS)) {
try {
// 执行业务逻辑
} finally {
semaphore.release();
}
} else {
// 走降级逻辑,比如返回 429 或 fallback 值
}
和 RateLimiter(Guava)或滑动窗口限流比,Semaphore 缺少什么
Semaphore 只管“当前有多少人在用”,不管“过去一分钟来了多少请求”。这意味着它无法应对突发流量(burst)、无法按 QPS 限流、也无法做平滑限流(如预热、冷启动)。它本质是并发数控制,不是速率控制。
- RateLimiter 支持预热期(
create(double, Duration))、支持平滑突发(acquire()返回等待时间) - 滑动窗口/令牌桶类限流器依赖时间戳和状态维护,
Semaphore完全无时间维度 - 若需按接口维度单独限流,
Semaphore需手动管理 Map,易出错且难监控
真正要落地限流,别硬套 Semaphore。它适合保护本地有限资源(如数据库连接池、文件句柄),而不是对外暴露的 API 接口。










