线程在AQS等待队列中被挂起后需依赖其他线程显式调用unpark()唤醒,核心机制是LockSupport.park()/unpark()配合volatile状态变量和CLH队列,唤醒由release()/signal()/unlock()等触发unparkSuccessor()扫描队列头后首个有效节点。

当线程在 AQS(AbstractQueuedSynchronizer)的等待队列中被挂起后,它不会自动醒来,而是依赖于其他线程显式调用 unpark() 来唤醒。核心机制是:AQS 利用 LockSupport.park()/unpark() 配合 volatile 状态变量 和 CLH 同步队列结构 实现精准唤醒。
唤醒由「释放同步状态」的操作触发
线程被唤醒的前提是持有锁或同步状态的线程完成工作并调用 release() / signal() / unlock() 等方法。这些方法最终会调用 AQS 的 unparkSuccessor():
- 该方法从队列头开始向后扫描,找到第一个 waitStatus (即非取消)的后继节点
- 调用 LockSupport.unpark(node.thread) 唤醒对应线程
- 注意:只唤醒一个线程(独占模式),共享模式下可能唤醒多个(如 doReleaseShared())
被唤醒的线程如何恢复执行
被 unpark() 唤醒后,线程会从 park() 调用处继续执行,但不等于立刻获得锁或进入临界区:
- 线程回到 AQS 的 acquireQueued() 或 doAcquireNanos() 等自旋/阻塞逻辑中
- 重新尝试通过 tryAcquire() 获取同步状态(比如 compareAndSetState)
- 若成功,将自己设为队列头、清理前驱节点;若失败,可能再次 park(取决于是否支持中断或超时)
关键细节:为什么不是 notify()?为什么能精准唤醒?
AQS 不用 Object.wait()/notify(),因为:
立即学习“Java免费学习笔记(深入)”;
- notify() 无法指定唤醒哪个线程,而 AQS 需按 FIFO 或优先级策略唤醒特定节点
- park/unpark 是基于 Thread 对象的配对操作,不受 synchronized 块限制,更灵活、无竞态
- 每个 Node 持有 thread 引用,unparkSuccessor() 可直接定位并唤醒目标线程
- volatile waitStatus 字段确保唤醒/取消等状态变更对其他线程可见
常见唤醒路径示例
以 ReentrantLock#unlock() 为例:
- 当前线程调用 unlock() → 走 AQS#release(1)
- release() 先 tryRelease()(修改 state,清 owner)→ 成功则调用 unparkSuccessor(head)
- unparkSuccessor() 找到 head.next 并 unpark 其 thread
- 被唤醒线程从 park 处返回 → 在 acquireQueued 中重试 tryAcquire → 成功则获得锁继续执行


















