semaphore 内部队列基于 aqs 同步队列实现 fifo 双向链表,线程在 acquire 失败时封装为 shared node 入队,通过自旋+park 阻塞;release 时释放许可并唤醒队首有效节点;公平模式禁止插队,非公平模式允许 cas 抢占;node 状态含 signal、cancelled 等,由竞争线程惰性清理。

Semaphore 内部队列通过 AQS(AbstractQueuedSynchronizer)的同步队列管理阻塞线程,本质是一个 FIFO 的双向链表,所有因获取许可失败而等待的线程都会被封装为 Node 节点加入该队列。
阻塞线程如何入队
当线程调用 acquire() 时,Semaphore 尝试以共享模式申请许可(即执行 tryAcquireShared):
- 若当前剩余许可数 > 0,直接 CAS 减少 state 值,获取成功,不入队;
- 若剩余许可为 0,且队列中已有等待者,或当前尝试失败后检测到队列非空(如
hasQueuedPredecessors()返回 true),则线程会构造一个 SHARED 类型的 Node,插入同步队列尾部; - 插入后线程进入自旋 + 阻塞循环:先尝试再次获取许可,失败则挂起(
LockSupport.park(this)),等待被唤醒。
唤醒机制依赖 AQS 的 release 流程
当某线程调用 release(),它会执行 tryReleaseShared 增加 state(即归还许可),随后触发:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- AQS 检查同步队列头节点后的第一个有效 SHARED 节点;
- 调用
unparkSuccessor唤醒该节点对应的线程; - 被唤醒线程重新进入
acquireSharedInterruptibly循环,再次尝试获取许可——此时若 state ≥ 所需 permits,即可成功出队并继续执行。
公平性对队列入队行为的影响
公平模式与非公平模式的关键差异就体现在“是否允许插队”:
-
公平模式(
new Semaphore(n, true)):每次 acquire 前都检查队列是否有前驱节点,有则直接入队,绝不抢占; - 非公平模式(默认):入队前会再尝试一次 CAS 获取许可,成功则跳过排队,提升吞吐但可能导致后续线程长期等待。
队列中线程的状态与清理
同步队列中的 Node 包含线程引用和 waitStatus 字段:
- waitStatus = 0:初始状态;
- waitStatus = SIGNAL(-1):表示后继节点需要被唤醒;
- 若线程被中断或超时取消,Node 会被标记为 CANCELLED(1),AQS 在后续操作中跳过该节点,并尝试清理前后指针;
- 队列是惰性维护的,没有独立线程管理它,所有清理动作都由参与竞争的线程顺带完成。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










