Exchanger是Java并发包中专为两个线程双向交换数据设计的轻量级同步工具,要求严格配对、一次交换、无内部缓冲,适用于双缓冲、流水线配对等严格双向协作场景。

Exchanger 不是用来“发数据”或“取数据”的,而是让两个线程在同一个点“面对面交货”——你拿我的,我拿你的,必须同时到场,缺一不可。它轻量、无锁、不缓存,特别适合双缓冲、流水线配对这类严格双向协作场景。
明确适用边界:只管两线程,不管多线程
Exchanger 的设计初衷就是服务一对线程。如果三个线程共用同一个实例:
- 前两个线程成功交换,第三个线程会一直阻塞,等下一个“空闲”线程来配对
- 无法保证它和谁配对,更不保证和上一轮是同一伙伴
- 容易因线程生命周期错位(如一个提前退出)导致另一个永久等待
所以实际部署时,建议固定绑定两个线程,避免线程池复用或动态调度带来的配对混乱。
必须加超时:防止单点故障拖垮整个流程
调用 exchange(V) 是无限等待的,生产环境绝不能裸用。推荐统一使用带超时的重载:
- exchanger.exchange(data, 3, TimeUnit.SECONDS) —— 最多等 3 秒,超时抛 TimeoutException
- 捕获异常后,应主动清理资源、记录日志,并决定是否重试或降级处理
- 不要依赖返回 null 判断超时(它不会返回 null,而是抛异常)
数据交接要干净:交换的是引用,不是副本
Exchanger 交换的是对象引用,不复制内容,这对大缓冲区(如 byte[])非常友好:
- 渲染线程填满 backBuffer 后,直接 exchange(backBuffer),显示线程拿到的就是同一块内存
- 交换后,原 buffer 的所有权立即转移,双方需约定好谁负责清空、重置或复用
- 切忌把 received 对象误当成自己传入的数据做后续判断——它来自对方,类型虽一致,语义完全不同
类型与空值:泛型保安全,null 要慎用
泛型声明如 Exchanger<byte[]> 能在编译期拦住类型混用,但运行时仍可能出错:
- 若绕过泛型用原始类型(raw type),exchange 时可能触发 ClassCastException
- null 允许交换,但极易引发 NPE;建议初始化时传哑值(如 new byte[0]),或用 Optional 封装业务数据
- 双方需事先约定是否允许 null,以及如何解读它的业务含义

















