Semaphore 在 Java 限流中本质是控制同一时刻最多几个线程执行关键逻辑,基于并发数硬性节制,简单轻量无状态,适用于单机资源保护,不适用于分布式或时间窗口限流。

Semaphore 在 Java 限流中不是做“时间窗口计数”或“令牌桶填充”,而是直接控制**同一时刻最多有几个线程能进入关键逻辑**——本质是基于并发数的硬性节制,简单、轻量、无状态,特别适合单机场景下的资源保护。
限流目标要明确:控的是“正在执行”的线程数
它不关心请求来了多少、过去一秒钟处理了几条,只管“此刻有没有空许可”。比如 new Semaphore(5),意味着最多 5 个线程能同时执行 acquire() 到 release() 之间的代码块。超出的线程会阻塞(或失败),直到有线程调用 release() 归还许可。
- 适合场景:调用强依赖的第三方接口、写入慢速数据库、生成大文件等真正消耗资源的操作
- 不适合场景:需要按秒/分钟统计 QPS 的全局限流、多节点共享阈值的分布式限流
- 关键提醒:必须部署在真正耗资源的位置,别在参数校验或日志打印前就 acquire,否则许可被“空占”,吞吐反而下降
acquire + try-finally 是安全底线
每次获取许可后,必须确保最终释放,哪怕业务逻辑抛异常。漏掉 release 就会导致许可永久丢失,后续所有线程卡死。
- 正确写法:acquire() 放在 try 块外,release() 必须放在 finally 块内
- 错误写法:把 acquire() 和 release() 都放进 try 块,异常时 release 不执行
- 更稳妥选择:用 tryAcquire(long, TimeUnit) 设置超时(如 300ms),超时直接拒绝,避免线程池被长期阻塞
灵活应对不同需求的常用组合
基础限流只是起点,结合几个方法就能适配更多实际条件:
立即学习“Java免费学习笔记(深入)”;
- 批量许可:acquire(3) 一次性拿 3 个,适合一次操作需占用多个资源单元(如批量写入 3 条记录)
- 动态调优:运行时调用 reducePermits(2) 缩减可用许可,或 release(2) 增加许可(注意 release 前需有对应 acquire)
- 实时监控:调用 availablePermits() 获取当前剩余许可数,配合 Prometheus 暴露指标,观察水位趋势
- 简易熔断:配合 AtomicInteger 统计单位时间内的失败率,连续失败超阈值时临时跳过 acquire,直接返回降级响应
单机有效,别误当分布式方案
每个 JVM 实例里的 Semaphore 是独立的。3 台机器各配 new Semaphore(10),实际总并发就是 30,不是 10。它解决不了跨进程协调问题。
- 适用边界:内部管理接口、低 QPS(
- 替代建议:高可用、多副本、QPS 波动大的系统,应选用 Redis+Lua 或 Sentinel 等中心化限流方案
- 部署提示:Spring 环境下务必声明为 @Bean 单例,禁止每次 new,否则许可无法共享


















