semaphore公平性本质是aqs fifo队列保障“等待最久者优先获许可”,通过hasqueuedpredecessors()判断入队顺序,release时固定唤醒队首节点,解决线程饥饿但不保证响应时间均等,适用高可预测性场景。

Semaphore 的公平性,本质是通过 AQS(AbstractQueuedSynchronizer)的 FIFO 同步队列实现“等待时间最长者优先获得许可”的调度逻辑,而非依赖线程优先级或系统调度器偏好。这种机制不保证执行时间均等,但确保入队顺序即服务顺序。
公平模式如何识别“等待最久的线程”
关键在于 hasQueuedPredecessors() 方法的调用判断:
- 每次线程调用
acquire()时,公平模式下的FairSync.tryAcquire()会先检查同步队列中是否存在比当前线程更早入队的节点 -
hasQueuedPredecessors()返回true,说明队列非空且头结点后有等待者 → 当前线程必须排队,不能抢占 - 只有当该方法返回
false(即队列为空,或当前线程是队列首节点)时,才尝试用 CAS 更新许可计数器
许可证分配严格遵循队列头部唤醒
释放许可时(release()),AQS 不随机唤醒,而是固定唤醒同步队列中第一个有效等待节点:
- 每个等待线程被封装为
Node,包含前驱/后继指针和waitStatus -
release()触发unparkSuccessor(head),仅唤醒head.next所指节点 - 被唤醒线程重新竞争许可,若成功则成为新 head;失败则继续等待——整个过程天然形成 FIFO 链式传递
为什么“等待时间最长”不等于“响应时间最短”
公平性只约束排队与唤醒顺序,不控制线程实际运行时机:
- 即使线程排在队首,仍需经历上下文切换、CPU 调度、JVM 线程状态转换等开销
- 若临界区操作耗时差异大(如有的线程 sleep(100),有的 sleep(10)),队列顺序无法抵消执行时间偏差
- AQS 唤醒与操作系统线程调度之间存在非精确对齐,可能造成微小错位,但不破坏整体 FIFO 语义
公平模式的实际适用边界
它真正解决的是资源长期紧张下的线程饥饿问题,而非提升实时性:
- 当许可数远小于并发线程数(例如 2 个许可、50 个线程持续争抢),非公平模式下新线程反复插队,旧线程可能永远等不到许可;公平模式则保障每个线程最多等待 N 轮即可轮到
- 适合对服务可预测性要求高、且能接受吞吐量下降约 20–30% 的场景,如金融交易网关的请求限流、审计日志写入门控
- 若临界区极短(










