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

Semaphore 的公平性,本质不是靠线程优先级或系统调度器,而是由 AQS(AbstractQueuedSynchronizer)维护的 FIFO 同步队列来保障——谁先调用 acquire(),谁就排在队列前面;释放许可时,只唤醒队首那个等待最久的线程。
公平模式如何识别“等待最久的线程”
关键在于每次获取许可前调用的 hasQueuedPredecessors() 方法:
- 该方法返回
true,说明队列非空,且当前线程前面已有其他等待者 → 必须入队,不能抢占 - 只有返回
false(即队列为空,或当前线程就是队列头节点)时,才尝试用 CAS 更新许可计数 - 这个判断发生在每个
acquire()调用入口,不依赖线程启动时间,只看请求时刻的队列状态
许可证分配严格按队列头部唤醒
release() 不随机选人,而是固定唤醒同步队列中第一个有效等待节点:
- 每个等待线程被封装为 AQS 中的
Node,含前后指针和状态字段 - 唤醒逻辑是
unparkSuccessor(head),仅作用于head.next - 被唤醒线程重新竞争许可:成功则成为新 head;失败则继续等待 —— 整个过程天然形成链式 FIFO 传递
公平 ≠ 响应快,也不等于执行时间均等
它只约束排队与唤醒顺序,不干预线程实际运行时机:
- 队首线程仍需经历上下文切换、CPU 调度、JVM 状态转换等开销
- 若临界区耗时差异大(比如有的 sleep(10),有的 sleep(100)),FIFO 无法抹平执行偏差
- AQS 唤醒与 OS 线程调度之间存在微小错位,但整体 FIFO 语义始终成立
公平模式真正解决的是线程饥饿问题
它适用于许可长期紧张的场景,而非追求实时响应:
- 当许可数远小于并发线程数(如 2 个 permit、50 个线程持续争抢),非公平模式下后进线程可能永远插队成功,导致队尾线程饿死
- 公平模式确保每个线程最多等待 N 轮(N ≈ 总线程数 / 许可数)即可轮到
- 典型适用场景包括金融交易网关限流、审计日志写入、配置加载门控等对可预测性要求高的环节
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











