Semaphore公平与非公平的本质差异在于许可获取的调度逻辑:公平模式强制FIFO排队,新线程须等待队首;非公平模式允许抢占,可能造成后到先得。

Java 中 Semaphore 的公平锁与非公平锁,本质差异不在“锁”本身,而在于线程尝试获取许可(permit)时的**调度决策逻辑**——这个逻辑由底层 AQS(AbstractQueuedSynchronizer)实现,直接影响线程是否排队、何时唤醒、能否插队。
公平模式:严格 FIFO 队列驱动的“守序型”调度
启用公平模式(new Semaphore(permits, true))后,每次调用 acquire() 时,AQS 会先检查同步等待队列中是否有前置节点(即更早等待的线程)。只有当队列为空或当前线程是队首时,才允许尝试 CAS 获取 state(剩余许可数);否则直接入队等待。
- 所有请求线程必须进入 CLH 队列,按到达顺序排队
- 释放许可(
release())时,仅唤醒队首线程,不跳过任何等待者 - 即使此时有空闲许可,新来的线程也不能“抢在老线程前面”获取
- 结果:调度可预测,饥饿风险极低,但上下文切换和队列管理开销略高
非公平模式:抢占优先的“机会型”调度(默认行为)
非公平模式(new Semaphore(permits, false) 或无参构造)下,线程在 acquire() 时**先尝试一次 CAS 抢占**——只要 state > 0,就直接扣减许可并成功返回,完全绕过队列检查。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 只有抢占失败(state == 0)时,才进入队列等待
- 释放许可后,唤醒等待队列头节点,但新线程仍可再次抢占,造成“后来者居上”
- 典型表现:短任务线程容易反复插队,长等待线程可能被持续压制
- 结果:吞吐量更高、延迟更短,尤其适合许可释放频繁、临界区轻量的场景
关键底层细节:AQS 的 tryAcquireShared 实现分叉
两种模式共享同一套 AQS 框架,但 tryAcquireShared 方法逻辑不同:
立即学习“Java免费学习笔记(深入)”;
- 公平版:强制调用
hasQueuedPredecessors()判断队列中是否存在前驱节点,为 true 就直接返回 -1(表示失败,需排队) - 非公平版:跳过该检查,直接执行
compareAndSetState(expect, update)尝试扣减许可 - 这个微小分支,决定了线程是“先看队列再动手”,还是“先动手再看队列”
不是“锁是否公平”,而是“许可分配策略是否有序”
Semaphore 本身不锁对象,它管理的是整数型许可。所谓公平性,是指对“许可申请请求”的处理顺序是否尊重时间先后。它不保证线程执行时间、不控制 CPU 调度,也不解决 IO 阻塞或业务逻辑卡顿带来的间接饥饿——这些需结合超时 acquire、监控排队长度等手段协同治理。

















