Semaphore 基于 AQS 实现,以 state 字段表示许可数,通过 CAS 修改 state 并配合 CLH 队列管理线程阻塞/唤醒;不使用 synchronized 或 monitor,本质是资源配额控制而非互斥锁。

Semaphore 在 Java 中不是靠“自己实现锁”,而是基于 AQS(AbstractQueuedSynchronizer) 构建的同步器,其核心是通过原子状态(state)控制许可数量,并配合等待队列管理线程阻塞与唤醒。
许可计数本质是 AQS 的 state 值
每个 Semaphore 实例内部持有一个 Sync 子类(继承自 AQS),真正用于表示“剩余许可数”的,就是 AQS 的 state 字段(int 类型)。调用 acquire() 时,尝试以 CAS 方式对 state 做递减;成功则获取许可,失败则进入 AQS 队列等待。释放许可(release())则是对 state 做原子递增,并唤醒等待队列中符合条件的线程。
- 公平模式下,AQS 使用 FIFO 队列,新线程会检查队列是否有等待者,有则排队;
- 非公平模式下,线程会先尝试抢占(CAS 修改 state),抢不到再入队;
- state 初始值即为构造时传入的 permits 数,每次 acquire 减 1,release 加 1。
线程阻塞与唤醒由 AQS 统一调度
当线程因许可不足而阻塞时,Semaphore 不直接调用 wait()/notify(),而是将当前线程封装为 Node 加入 AQS 的 CLH 同步队列,并调用 LockSupport.park() 挂起。一旦其他线程调用 release() 使 state 增加,AQS 会从队列头开始遍历,唤醒足够数量的等待线程(按 acquire 参数决定唤醒几个),被唤醒线程重新竞争 state。
- 注意:Semaphore 支持一次 acquire 多个许可(如
acquire(3)),此时需 state ≥ 3 才能成功; - 唤醒不保证顺序,除非启用公平模式;
- park/unpark 是 JVM 层级的线程调度原语,比 Object monitor 更轻量、无竞态。
底层无 synchronized 或 monitor 锁参与
Semaphore 的全部同步逻辑完全托管给 AQS,自身不使用 synchronized 块,也不依赖对象监视器(monitor)。它的“锁语义”是逻辑上的资源配额控制,而非临界区互斥——多个线程可同时持有许可并并发执行,只要总数量不超过上限。
立即学习“Java免费学习笔记(深入)”;
- 这与 ReentrantLock / synchronized 有本质区别:后者保证“同一时刻最多一个线程执行”,前者保证“同一时刻最多 N 个线程执行”;
- 因此,Semaphore 适合限流、池化(如数据库连接池)、信号协调等场景,而非保护共享变量的读写一致性;
- 若需保护共享数据,仍需配合 volatile、AtomicXXX 或显式锁。
tryAcquire 和 interruptible 等行为均由 AQS 提供支持
诸如 tryAcquire(long timeout, TimeUnit unit) 这类带超时的方法,底层调用的是 AQS 的 doAcquireNanos,它在 park 前设置超时 deadline,循环中检测是否超时或被中断;而 acquireInterruptibly() 则在 park 后检查中断状态并抛出 InterruptedException。
- 所有这些能力都复用 AQS 已验证的线程排队、中断响应、超时控制机制;
- 开发者无需关心底层线程调度细节,只需理解 state 含义和 acquire/release 的语义;
- 这也说明:Java 并发包中多数同步工具(CountDownLatch、CyclicBarrier、ReentrantLock)都共享同一套 AQS 底层基础设施。


















