semaphore本质是动态配额池,通过acquire扣减、release跨线程返还许可,支持批量操作与超额释放;reducepermits可收缩配额,expandpermits需自定义实现。

Semaphore 的许可不是静态分配的,而是通过原子操作在运行时动态发放与回收,本质是一套“配额池”机制,而非传统意义上的锁。
许可发放:acquire() 是配额扣减,不是资源绑定
每次调用 acquire()(或 acquire(int permits)),Semaphore 就从内部计数器(AQS state)中减去对应数量的许可。这个过程是原子的,但不检查资源是否真实可用、也不校验调用线程身份——它只管“有没有额度”。
- 初始 permits = 5,3 个线程已成功 acquire(1),此时
availablePermits()返回 2 - 第 4 个线程调用
acquire(3):因只剩 2 个可用许可,该线程将阻塞,直到有足够许可被释放 - 支持一次申请多个许可,适合批量场景(如同时读取多个文件句柄、占用多个连接)
许可回收:release() 是配额返还,可跨线程执行
release() 操作会原子性地增加计数器,且不要求执行线程必须是之前 acquire 的线程——这和 ReentrantLock 有本质区别。
- 线程 A acquire 后交由线程 B 处理任务,B 完成后调用 release,完全合法
- 可用于异步回调、超时兜底、资源池归还等灵活场景
- release(permits) 可一次性返还多个许可,甚至能“超额释放”(比如把许可数从 2 加到 10),但需业务逻辑确保合理性
动态调整许可总量:reducePermits 与 expandPermits
标准 Semaphore 不提供直接扩容方法,但 reducePermits(int reduction) 是官方支持的“收缩”手段;扩容则需继承并暴露类似逻辑(如自定义 expandPermits)。
-
reducePermits(2)直接将 state 减 2,不唤醒等待线程,也不影响已持有许可的线程 - 该方法为 protected,需子类封装后使用,典型用于运维限流降级(如突发流量下主动收紧并发窗口)
- 若减后 state Error(非 Exception),属于严重逻辑错误,应严格校验 reduction 值
公平性影响的是排队秩序,不是许可本身
公平模式(new Semaphore(n, true))仅改变等待线程被唤醒的顺序(FIFO),不影响许可的发放/回收逻辑或线程安全性。
- 非公平模式吞吐更高,允许刚释放许可的线程“插队”重入,适合日志写入等高吞吐场景
- 公平模式避免饥饿,适合对响应延迟敏感的系统(如支付请求排队)
- 无论哪种模式,acquire/release 的原子性、可见性和线程安全均由 AQS 保障
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











