Exchanger 使线程迅速从 RUNNABLE 进入 WAITING/TIMED_WAITING 状态,主动放弃 CPU 并脱离操作系统就绪队列;其“极快挂起”源于轻量级配对检测与 LockSupport.park(),但缺乏配对生命周期管理,导致超时后慢线程仍持续阻塞。

Exchanger 本身不会把线程“挂起在操作系统的就绪队列中”——恰恰相反,它会让线程**离开就绪队列、进入阻塞状态**。所谓“极快挂起”,其实是线程在调用 exchange() 后迅速从 CPU 可调度状态(RUNNABLE)转为等待配对的**WAITING 或 TIMED_WAITING 状态**,不再参与操作系统调度,也不再消耗 CPU 时间片。
Exchanger 阻塞的本质是线程状态切换
当一个线程调用 exchanger.exchange(x) 时:
- 若无配对线程,它会通过 CAS 自旋 + 条件等待(底层基于
LockSupport.park())主动放弃 CPU - JVM 将其标记为 WAITING(无超时)或 TIMED_WAITING(带超时),操作系统线程调度器立即将其移出就绪队列
- 该线程不再被分配处理器时间,直到另一个线程在同一 Exchanger 实例上调用
exchange()并完成配对
单向长尾慢计算加剧“半悬挂”现象
所谓“单向长尾”,指仅有一方线程能及时到达交换点,另一方因计算耗时、I/O 延迟或逻辑卡顿而严重滞后(如 10 秒后才调用)。此时:
- 快线程在 exchange 处阻塞,状态变为 TIMED_WAITING(若设了超时)或 WAITING(若未设)
- 慢线程仍在 RUNNING 状态执行长尾任务,尚未进入 exchange,因此不构成配对
- 快线程虽已“挂起”,但它的阻塞是干净的:不占 CPU、不抢锁、不自旋耗电;问题在于它无法推进业务,且无人通知慢线程“你已被放弃”
为什么看起来“挂起得极快”?
这不是性能优势,而是同步语义的即时体现:
- Exchanger 的配对检测是轻量级的(基于原子变量和单次 park),无复杂队列维护或唤醒竞争
- 从调用 exchange 到线程进入 WAITING 状态,通常只需几十纳秒到微秒级,远快于 synchronized 或 Lock 的争用路径
- 所以“快”是指状态切换快,不是响应快;反而暴露了设计刚性:它不做重试、不支持取消、不提供配对监控
真正影响系统可观测性的不是挂起速度,而是不可解除性
即使快线程超时退出,慢线程仍卡在原地等待——因为 Exchanger 没有全局配对生命周期管理:
- 超时只清理当前线程本地等待节点,不通知或中断对方
- 慢线程的 park() 依然有效,JVM 线程 dump 中持续显示
java.util.concurrent.Exchanger$Node等待 - 没有内置机制判断“对方已放弃”,也无法触发 fallback 或补偿逻辑


















