semaphore 依托 aqs 构建 fifo 同步队列实现高效许可调度:state 字段原子管理许可数,线程封装为 node 入队等待,公平模式严格遵循先到先得,非公平模式允许插队提升吞吐,release 时精准唤醒队首线程,动态调整许可数与队列解耦。

Java 中 Semaphore 的内部队列设计,是其能在高并发下保持响应性与可控性的关键所在。它不靠简单轮询或忙等待,而是依托 AQS(AbstractQueuedSynchronizer)构建的 FIFO 等待队列,实现线程阻塞、唤醒与许可分配的高效协同。
基于 AQS 的同步队列保障有序调度
AQS 为 Semaphore 提供了统一的线程排队与状态管理机制。其内部使用一个 volatile int 类型的 state 字段表示当前可用许可数,所有 acquire/release 操作最终都映射为对 state 的 CAS 修改。当许可不足时,请求线程会被封装为 Node 节点,安全地加入 AQS 的双向同步队列尾部——这个过程是原子且无锁的。
- 队列按 FIFO 原则组织,确保等待时间长的线程优先被唤醒(尤其在公平模式下)
- 每个 Node 包含线程引用、等待状态(CANCELLED、SIGNAL 等)和前后指针,支持快速插入、移除与状态更新
- 线程挂起由 JVM 底层
LockSupport.park()实现,避免 CPU 空转,降低资源开销
公平与非公平模式影响队列竞争行为
队列本身存在,但是否严格遵循排队顺序,取决于构造时指定的 fair 参数:
- 公平模式:每次
acquire()都先检查队列是否非空,若有等待者则直接入队,不尝试抢占;保证“先到先得”,避免饥饿,适合对延迟敏感或 SLA 明确的场景 - 非公平模式(默认):先尝试 CAS 获取许可,失败后再入队;允许新线程“插队”成功,提升吞吐量,但可能使长等待线程延后获取许可
释放许可触发精准唤醒机制
release() 不只是增加 state,还会主动唤醒队列头部符合条件的线程:
- 唤醒逻辑在 AQS 的
unparkSuccessor()中完成,只唤醒一个处于 SIGNAL 状态的后继节点 - 被唤醒线程重新尝试获取许可,若成功则继续执行;否则再次入队或自旋(取决于实现细节)
- 这种“一对一唤醒 + 再竞争”设计,避免惊群效应(thundering herd),也防止许可被过度分发
动态许可调整与队列兼容性
Semaphore 支持运行时动态增减许可数(如 reducePermits()、drainPermits()),这些操作直接修改 state,无需操作队列本身:
- 增加许可(如
release())可能触发唤醒,但不会改变队列结构 - 减少许可(如
reducePermits())仅降低计数器,已排队线程仍按原顺序等待,语义清晰且线程安全 - 队列始终只承载“等待许可”的诉求,与许可数量变化解耦,提升了扩展性和稳定性
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











