AQS在Semaphore中通过volatile int state字段表示剩余许可证数,基于CAS实现无锁扣减与释放;公平性由tryAcquireShared是否检查队列前置节点决定,CAS操作本身始终原子。

AQS(AbstractQueuedSynchronizer)在 Semaphore 中通过其内部的 state 字段表示当前可用许可证数量,并借助 CAS(Compare-And-Swap)实现线程安全的动态扣减,不依赖锁,而是基于 volatile + CAS 的无锁机制。
state 字段作为许可证计数器
Semaphore 的公平/非公平模式都继承自 AQS,其核心状态由 AQS 的 volatile int state 维护——该值即剩余许可证数。初始化时,state = permits;每次 acquire() 尝试扣减 1(或指定数量),release() 则增加对应数量。
- state 是 volatile 的,保证多线程可见性
- 所有修改均通过 AQS 提供的
compareAndSetState(int expect, int update)完成,底层调用 Unsafe 的 CAS 指令 - 例如:尝试获取 1 个许可时,循环执行
if (getState() >= 1 && compareAndSetState(current, current - 1))
tryAcquireShared 方法实现非阻塞扣减逻辑
Semaphore 使用共享模式(Shared Mode),重写 AQS 的 tryAcquireShared(int acquires)。该方法不直接修改 state,只做“检查+CAS”原子尝试:
- 读取当前
state值(记为s) - 计算新值:
newCount = s - acquires - 若
newCount ,说明许可不足,返回负数(表示失败) - 否则,用 CAS 尝试将 state 从
s更新为newCount;成功则返回非负数(表示获取成功) - 失败则重试(自旋),直到成功或判定需入队等待
失败时进入 AQS 同步队列等待
当 CAS 扣减失败(如并发竞争导致 state 已被其他线程改写),且当前线程选择阻塞(如调用 acquire() 而非 tryAcquire()),AQS 会将该线程封装为 Node 加入同步队列:
- Node 入队过程本身也通过 CAS 保证线程安全
- 后续由前驱节点唤醒机制触发重试,再次调用
tryAcquireShared - 释放操作(
release())调用tryReleaseShared,同样 CAS 增加 state,并可能唤醒队列中等待的节点
公平性由 tryAcquireShared 的前置判断控制
公平模式下,tryAcquireShared 会先检查同步队列是否有等待节点(hasQueuedPredecessors());若有,则直接返回失败,避免插队。而非公平模式跳过此检查,允许新请求直接 CAS 尝试扣减——这提升了吞吐,但可能造成饥饿。
- 公平性不影响 CAS 本身,只影响“是否允许参与 CAS 竞争”的时机
- CAS 扣减动作始终是原子的,无论公平与否
- 真正决定调度顺序的是 AQS 队列的 FIFO 特性与唤醒逻辑
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











