semaphore公平模式保障顺序但吞吐低22%、延迟高且尾部延迟易恶化,非公平模式吞吐高、延迟低但可能线程饥饿;选型应依业务是否真需保序:金融交易等选公平,api限流等选非公平。

Java 中 Semaphore 的公平与非公平模式,性能差异主要体现在吞吐量、延迟和线程调度开销上,不是“快或慢”的简单对比,而是不同调度策略带来的系统行为权衡。
吞吐量:非公平模式通常高 10%–30%
实测数据显示(JDK 21,4 核,1000 线程争抢 5 个许可):
- 非公平模式平均吞吐量比公平模式高出约 22%,部分压测场景甚至达 30%
- 差距主因是公平模式每次
acquire()都要调用hasQueuedPredecessors()检查队列状态,增加 CPU 分支判断和内存读取开销 - 公平唤醒只放行队首线程,导致更多上下文切换;非公平模式释放后新线程可立即 CAS 抢占,减少阻塞链路
延迟表现:非公平更短但波动大,公平更稳但整体偏高
在 P99 延迟指标上:
- 非公平模式延迟低约 37%,尤其适合临界区执行时间短(如计数、日志打点、缓存读)的场景
- 公平模式延迟可预测性强——最大等待时间基本等于(队列长度 × 平均持有时间),适合对尾延迟敏感的系统
- 但若许可稀缺且请求持续涌入,公平模式下队列不断拉长,尾部线程的实际延迟会显著上升
线程饥饿与调度成本的反向关系
公平性不免费,它用调度确定性换来了资源利用率下降:
- 公平模式彻底规避饥饿:只要不永久阻塞,每个线程最终都能拿到许可
- 非公平模式下,高频抵达的新线程可能反复插队,使队列尾部线程长期得不到许可(典型表现为
Thread.State.WAITING堆栈中大量AbstractQueuedSynchronizer$ConditionObject.await) - 这种“机会型”调度减少了排队和唤醒次数,但代价是牺牲了请求时间顺序的语义保证
怎么选?看业务是否真需要“先到先得”
不要因为“公平听起来更安全”就默认开启。关键判断点:
- 选公平:金融交易指令排序、审计日志落盘、配置加载锁等必须保序且容忍低吞吐的场景
- 选非公平:API 限流、静态资源服务、批量查询等响应快、允许乱序、追求高吞吐的通用场景
- 低竞争时(如初始化阶段仅几个线程争 1 个许可),公平模式几乎无性能损失,可放心用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











