Exchanger 是用于两个线程在同步点必须严格交换对象的并发工具,适用于双缓冲、配对型生产者-消费者等场景;它不传数据而是“不见不散”式配对,需防空指针、超时和线程数错配。

Exchanger 是什么,什么时候该用它
它不是用来“传数据”的,而是两个线程在某个同步点**必须互相交换对象**时才用的。比如 A 线程生成一批数据要交给 B 处理,B 同时也有一批结果要还给 A —— 二者必须等对方到位才能换,不能丢、不能复制、不能跳过。常见于双缓冲场景(如图形渲染、流式数据处理),或者严格配对的生产者-消费者变体。
别把它当 BlockingQueue 或 TransferQueue 用:那些是“发出去就完事”,Exchanger 是“不见不散”。一旦一个线程调用 exchange(),它就会卡住,直到另一个线程也调用同个 Exchanger 实例的 exchange()。
怎么写一个安全可用的 exchange() 调用
核心就一句:exchanger.exchange(data),但实际用起来得防三件事:空指针、超时、线程不对等。
- 永远传非 null 对象,
Exchanger不检查 null,但另一端拿到 null 很可能直接 NPE - 加超时,避免死等:
exchanger.exchange(data, 3, TimeUnit.SECONDS);返回 null 表示超时,此时你要决定是重试、抛异常,还是走降级逻辑 - 确保只有**恰好两个线程**参与同一个
Exchanger实例;三个线程调用会出乱子——第三个线程会和前两个中“还没配对成功”的那个配对,逻辑就崩了 - 泛型类型必须一致,
Exchanger<string></string>和Exchanger<integer></integer>混用编译不过,但运行时如果靠 raw type 绕过,会在exchange()时抛ClassCastException
为什么 exchange() 有时卡住不动
最常见原因是线程生命周期没对齐。比如主线程启了 workerA 和 workerB,但 workerA 执行完 exchange() 就退出了,workerB 却还在等——它永远等不到第二次配对(因为 Exchanger 每次只撮合一对)。
立即学习“Java免费学习笔记(深入)”;
- 确认两个线程都活着且确实执行到了
exchange();加日志或断点验证,别只看启动代码 - 不要在一个线程里反复调用
exchange()期望复用——它每次都是新配对,上一轮的 partner 已经走人了 -
Exchanger内部无队列,不缓存数据;如果 partner 线程被中断,当前线程会收到InterruptedException,记得捕获并清理资源 - 在 ForkJoinPool 或虚拟线程(Loom)里用要小心:线程可能被回收或挂起,导致配对延迟甚至失败
和 SynchronousQueue.compareAndSet() 或 Phaser 比有什么区别
它比 SynchronousQueue 更轻量,没有内部节点管理开销,适合高频、短生命周期的双线程交换;但它不支持多线程、不支持公平策略、也不提供 peek/drain 等操作。
和 Phaser 完全不是一类东西:Phaser 是协调多个线程到达某阶段,不传数据;Exchanger 只服务两个线程,且必须交换值。
- 如果你需要交换的是 byte[] 或大对象,注意 JVM 堆压力——
Exchanger不做拷贝,谁传进来谁负责对象生命周期 - JDK 8+ 的
Exchanger在单核或高竞争下性能略降,底层用了类似 CLH 的自旋+阻塞混合机制,别指望它在 100 个线程里跑出好成绩 - 没有“取消配对”API,也没法查当前有没有线程在等;真要动态控制,得自己包一层带状态标记的 wrapper
真正难的从来不是调用那一行代码,而是保证两个线程在时间、生命周期、错误恢复上始终能严丝合缝地碰上。










