当semaphore许可耗尽(state==0)时,acquire()线程被aqs阻塞:先原子减state,若结果

当 Semaphore 的许可耗尽(即内部计数器 state == 0)时,后续调用 acquire() 的线程会被阻塞,这一过程由底层 AQS(AbstractQueuedSynchronizer)自动完成,无需手动干预。
许可耗尽的判定条件
每次调用 acquire(),Semaphore 会尝试对 AQS 的 state 字段执行原子减法操作。若减后结果 小于 0(例如当前 state = 0,再减 1 得 -1),说明无可用许可,触发阻塞逻辑。
- 非公平模式下:直接 CAS 尝试扣减;失败即进入阻塞流程
- 公平模式下:先检查同步队列中是否有更早等待的线程;若有,则跳过抢占,直接入队
阻塞发生的实际路径
一旦判定许可不足,线程不会忙等,而是被封装为 Node 节点,插入 AQS 的 FIFO 同步队列尾部,并将线程状态设为 WAITING。此时线程被挂起(通过 LockSupport.park()),不再参与 CPU 调度。
- 该队列是双向链表结构,由 AQS 维护,不依赖操作系统线程调度器的等待队列
- 线程处于 park 状态,内存可见性由 AQS 的 volatile state 和锁机制保障
唤醒依赖 release 操作
只有其他线程调用 release() 增加 state 并成功唤醒队列头部节点时,被阻塞的线程才会被 unpark,重新尝试获取许可。
- 唤醒不是立即执行,而是由 AQS 在 CAS 成功后调用
unparkSuccessor() - 被唤醒线程会再次竞争 state,可能仍失败(如多个线程同时被唤醒而许可只释放一个),需循环重试
注意异常与中断响应
acquire() 是可中断的:若线程在阻塞期间收到中断信号,会抛出 InterruptedException 并退出等待,同时清理自身在队列中的节点。
- 使用
acquireUninterruptibly()可绕过中断,但需承担无法及时响应终止请求的风险 - 未在 finally 块中调用
release()可能导致许可永久丢失,使后续线程持续阻塞
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











