exchanger要求两个线程严格同步抵达才能交换,必须成对使用、独占实例、数据就绪后调用,并强制设置超时以避免死锁。

Exchanger 不是“发完就走”的通信工具,它的交换时机由两个线程**严格同步抵达**决定。用错时机,轻则逻辑错乱,重则线程永久挂起。关键不在怎么写 exchange(),而在于让两个线程在该换的时候,刚好都到了。
必须成对出现,不能多也不能少
Exchanger 只认两个线程:A 到了等 B,B 到了等 A。第三个线程调用 exchange(),它会一直等——因为没有配对者。这不是排队,是“一对一握手”。常见误用:
- 把 Exchanger 用在动态增减线程的场景(比如线程池里随意提交任务)
- 同一实例被多个线程组复用(如 A-B 用一个 exchanger,C-D 也用同一个)
- 某线程因异常提前退出,另一线程还在死等
建议:每个双线程协作单元独占一个 Exchanger 实例;线程生命周期与 Exchanger 使用周期对齐。
交换点不是越早越好,得看数据就绪状态
别在线程刚启动、变量还没初始化完就急着调 exchange()。交换前双方必须已准备好要交出的数据,否则可能传 null 或脏值。典型反例:
- A 线程刚 new 出 buffer 就 exchange(buffer),但还没往里写数据
- B 线程在 exchange() 后才开始解析收到的对象,却没检查是否为 null 或是否符合预期结构
建议:把 exchange() 放在数据真正生成完毕之后;接收方拿到对象后做必要校验(如非空、格式合法),不要假设对方一定给对了。
超时不是可选项,而是生产环境必需项
exchange(V) 会无限等待,一旦某一方卡住(GC 暂停、IO 阻塞、逻辑 bug),整个协作流程就僵死。带超时的版本才是可靠选择:
- 优先使用 exchange(V, timeout, unit),例如 3 秒 是多数流水线场景较稳妥的起点
- 捕获 TimeoutException 后,需有明确降级路径:重试?跳过本轮?上报告警?不能只打印日志就结束
- 同时处理 InterruptedException,并恢复中断状态(Thread.currentThread().interrupt()),避免掩盖调度意图
别在 exchange() 里干重活
exchange() 本身应是瞬时操作。如果在传入对象前做了耗时计算,或在拿到对方数据后立刻启动复杂解析,等于把阻塞时间转嫁给了配对线程——它也在等你完成 exchange() 才能继续。
- 预处理和后处理逻辑应放在 exchange() 调用前后,而非参数构造或返回值消费过程中
- 例如图像双缓冲场景:A 完成 bufferA 的缩放 → 立刻 exchange → 再启动 bufferB 的下一轮缩放
不复杂,但容易忽略。











