exchanger的核心是“配对即交换”,而非先后顺序;它通过cas和park/unpark实现两线程在同步点原子交换,具备happens-before内存可见性,并支持超时机制避免无限等待。

Exchanger 的核心不是“谁先谁后”,而是“配对即交换”。它不维护全局队列或轮询机制,而是让两个线程在同一个同步点上彼此发现、原子交换,整个过程由底层 CAS 和线程挂起/唤醒机制保障。
等待是主动让出 CPU,不是忙等
当第一个线程调用 exchange(),它不会循环检查是否有另一个线程到来。而是立即尝试用 CAS 将自己的数据写入内部槽位(slot),若成功,就把自己挂起(park),进入 WAITING 状态,释放 CPU 资源。这个挂起由 JVM 底层线程调度器管理,不消耗 CPU。
- 挂起前会设置一个 volatile 字段(如
match或parked)标记等待状态 - 第二个线程到达时,通过 CAS 尝试“配对”——读取第一个线程的数据,把自己的数据写入对方槽位,并唤醒对方
- 唤醒操作触发 JVM 解除第一个线程的 park 状态,使其恢复执行
配对只发生在同一 Exchanger 实例的同一“槽位”
Exchanger 内部采用“单槽位 + arena 扩展”结构。JDK 1.6 后为应对高并发,引入 arena(竞赛场)机制:当多个线程频繁争抢同一槽位失败时,自动切换到不同索引的备用槽位,避免 CAS 冲突。但每个成功的交换仍严格限定为两个线程、一个槽位、一次原子交换。
- 线程 A 进入槽位 0,挂起等待
- 线程 B 到达,优先尝试槽位 0;若发现 A 已在其中,完成交换并唤醒 A
- 若槽位 0 正被占用或冲突频繁,B 可能转向槽位 1、2…,但只会与该槽位中唯一等待的线程配对
交换动作具备内存可见性保证
Exchanger 在交换前后插入了内存屏障(Memory Barrier)。这意味着:
- 线程 A 在
exchange()调用前对共享变量的所有写操作,对线程 B 在交换后读到的数据及后续操作都可见(happens-before 关系) - 同理,线程 B 交换前的写,对线程 A 交换后的操作也可见
- 这种保证不需要额外加锁或 volatile 声明,由 Exchanger 自身实现提供
超时交换本质是带时间约束的等待
使用 exchange(x, timeout, unit) 时,线程在挂起前会记录绝对截止时间。挂起后,JVM 不仅响应唤醒信号,还会在超时时刻自动唤醒该线程,并抛出 TimeoutException。此时 Exchanger 内部会清理该槽位状态,确保不阻塞后续线程。
- 超时不是轮询判断,而是依赖操作系统级定时器 + JVM 线程中断机制
- 即使另一个线程最终到达,只要本线程已超时退出,就不会再参与本次交换
- 超时后槽位重置,不影响下一对线程使用











