aqs中waiting→runnable迁移由locksupport.unpark触发,经os调度器将线程移入就绪队列,全程基于cas和缓存一致性协议,不涉及cpu总线锁。
这个问题存在概念混淆与技术错位,需要先厘清几个关键事实:
一、AQS 的 WAITING → RUNNABLE 迁移不涉及硬件总线锁
Java 线程状态从 WAITING(如调用 LockSupport.park())转为 RUNNABLE,本质是 JVM 层的线程唤醒行为,由操作系统调度器完成:
- AQS 在
unparkSuccessor()中调用LockSupport.unpark(thread) - JVM 调用 OS 级 API(如 Linux 的
futex_wake或pthread_cond_signal) - 操作系统将线程从等待队列移入就绪队列,后续由 CPU 调度器分配时间片
✅ 这一过程完全不触发 CPU 总线锁(如 LOCK# 信号),也不涉及内存总线争用。总线锁仅出现在 x86 架构下对未缓存内存执行原子指令(如LOCK INC [addr])时——而 AQS 所有核心操作(state 更新、head/tail 修改)均基于 CAS(CompareAndSwap),底层使用的是 缓存一致性协议(MESI)+ 处理器自带的轻量级原子原语(如 cmpxchg),非总线锁。
二、购物车结算场景中真正影响线程状态迁移的,是同步策略设计,不是硬件锁
多层购物车结算(如“合并跨店铺订单→校验库存→扣减优惠→生成支付单”)若用 AQS 实现排队控制,典型做法是:
- 自定义一个
CartCheckoutSync类继承AbstractQueuedSynchronizer - 用
state == 0表示无结算任务在执行,state == 1表示有线程正在处理 -
tryAcquire()尝试 CAS 设置state = 1;失败则入队等待 -
tryRelease()将state设回0,并唤醒队首线程
此时线程从 WAITING → RUNNABLE 的时机,取决于:
- 前序线程是否已调用
unlock()(即tryRelease()成功) - AQS 是否成功修改
state并调用unpark() - 操作系统是否及时将目标线程标记为就绪
⚠️ 这中间没有、也不需要硬件总线锁介入。强行引入总线锁反而会严重拖慢吞吐,违背高并发设计初衷。
三、若真遇到性能瓶颈,该查的方向是:
- ✅ 锁粒度是否过大:是否用一把全局锁串行化所有购物车结算?应按用户 ID / 订单 ID 分片加锁
- ✅ 是否发生虚假唤醒或超时竞争:
await()+signal()配合不当导致线程反复 park/unpark - ✅ GC 停顿干扰:长时间 GC 可能延迟
unpark()执行,使线程卡在WAITING - ✅ CPU 调度延迟:高负载下 OS 调度器响应滞后,
unpark()后仍需等待调度 - ❌ 不查“总线锁竞争”——JVM 和现代 CPU 架构早已屏蔽了这一层级的细节,且它不是瓶颈根源
四、想验证线程状态真实迁移路径?推荐方法:
# 1. 开启 JVM 线程 dump(无需停机)
jstack -l <pid> > thread_dump.log
# 2. 观察关键线程状态(注意不是 WAITING,而是 BLOCKED 或 TIMED_WAITING)
"CartProcessor-3" #tid=0x00007f8a1c0b2000 nid=0x1a34 waiting on condition [0x00007f8a1a9e9000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
# 3. 结合 async-profiler 抓取锁竞争热点
./profiler.sh -e lock -d 30 -f lock-report.html <pid></pid></pid>
报告中只会显示 ReentrantLock$NonfairSync.acquire() 或 AbstractQueuedSynchronizer.acquireQueued() 的热点,绝不会出现“bus_lock”或“LOCK#”字样。
不复杂但容易忽略:AQS 是软件抽象层,它的“排队”是逻辑队列,它的“唤醒”是调度信号,它的“竞争”是 CAS 循环——这些全部运行在 JVM 与 OS 协同构建的虚拟执行环境里,和硬件总线锁不在同一维度。










