acquirequeued通过检查前驱节点是否为head及waitstatus状态,确保线程仅在前驱设为signal后才安全挂起,避免忙等并保障唤醒及时性。

acquireQueued 方法是 AQS(AbstractQueuedSynchronizer)中实现独占式获取同步状态的核心逻辑,它决定了线程在争抢锁失败后,是继续自旋等待,还是挂起进入阻塞状态。这个切换不是由 AQS 主动“选择”自旋或挂起,而是基于队列中前驱节点的状态,**以避免忙等、减少 CPU 消耗,同时保证唤醒及时性**。
前驱节点是否为 head 是关键判断点
线程入队后,acquireQueued 会反复检查自己是否是同步队列的第二个节点(即前驱是 head)。只有当前驱是 head,才说明“轮到自己尝试获取锁”,此时会再次调用 tryAcquire。若成功,就设置自己为新 head 并退出;若失败,则进入下一步判断。
- 如果前驱节点状态是 CANCELLED(已取消),就跳过它,向前找第一个非取消的前驱
- 如果前驱是正常节点,且其 waitStatus == 0 或 PROPAGATE,则尝试将其设为 SIGNAL(表示“我挂起后请通知我”)
- 只有在成功设置前驱为 SIGNAL 后,才会调用 LockSupport.park(this) 挂起当前线程
挂起前必须确保前驱能负责唤醒
AQS 不允许线程无保护地挂起。挂起前,必须保证前驱节点处于可通知状态(即 waitStatus == SIGNAL),否则可能永久休眠。这就是为什么 acquireQueued 在 park 前会循环执行 shouldParkAfterFailedAcquire —— 它不断校验并修正前驱状态,直到确认前驱能触发唤醒(比如前驱释放锁时会 unpark 后继)。
- 若前驱状态为 CANCELLED,当前线程会帮助清理队列,跳过无效节点
- 若前驱状态为 0,当前线程会 CAS 将其设为 SIGNAL,再重试一次获取;失败则继续循环
- 只有前驱状态稳定为 SIGNAL,才真正 park —— 这是安全挂起的前提
没有主动“自旋”,只有有限重试与条件挂起
AQS 默认不提供忙等待(busy-spin)机制。所谓“自旋”,实际只是在 park 前的几次快速重试(例如在 shouldParkAfterFailedAcquire 中最多尝试一两次 CAS 设置前驱状态),并非持续占用 CPU 的 while(true) 循环。
- 重试目的不是靠 CPU 时间等锁,而是避免因并发修改导致的设置失败
- 一旦前驱被成功标记为 SIGNAL,线程立刻 park,不再轮询
- 真正的“自旋优化”需额外实现(如 AbstractOwnableSynchronizer 配合 LockSupport.parkNanos 做短时自旋),AQS 基础逻辑不包含该行为
唤醒依赖前驱节点的 release 流程
线程挂起后,能否及时恢复,取决于前驱节点在释放锁时是否正确执行了 unparkSuccessor。该方法会找到队列中第一个非取消的后继节点,并调用 LockSupport.unpark。
- head 节点 release 后,会唤醒它的 next(即当前线程所在节点)
- 被 unpark 的线程从 park 返回,重新进入 acquireQueued 循环,再次 tryAcquire
- 若此时锁已被其他线程抢占,它可能再次 park —— 这就是“挂起-唤醒”的完整闭环
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











