Exchanger是Java并发包中专用于两个线程在同步点交换数据的工具类,适用于双缓冲、成对生产者-消费者等严格一对一配对场景,不支持多线程或广播交换。

Exchanger 是什么,它适合解决哪类问题
Exchanger 是 Java 并发包(java.util.concurrent)中一个轻量级的线程间协作工具,专用于**两个线程在某个同步点交换数据**。它不是通用的共享容器,也不支持多于两个线程——一旦第三个线程调用 exchange(),它会一直阻塞直到另一个线程也到达该点并配对成功。
典型适用场景包括:双缓冲数据处理(如一个线程填充缓冲区 A,另一个线程消费缓冲区 B,然后双方交换)、生产者-消费者成对协作、测试中模拟双向握手等。
注意:Exchanger 不是 BlockingQueue 或 Semaphore 的替代品;它强调“恰好两人”、“一次配对”、“单次交换”,误用于多线程轮换或重复交换会导致死锁或不可预期等待。
如何正确初始化和调用 exchange() 方法
声明时需指定泛型类型,确保两端线程交换的数据类型一致:
立即学习“Java免费学习笔记(深入)”;
Exchanger<byte[]> exchanger = new Exchanger<>();
每个线程在同步点调用 exchange(V x),传入自己要交出的数据,并返回对方交来的数据:
- 线程 A 调用
exchanger.exchange(bufferA)→ 阻塞,直到线程 B 也调用exchange() - 线程 B 调用
exchanger.exchange(bufferB)→ 两者配对,A 收到bufferB,B 收到bufferA - 若其中一方超时或中断,另一方会收到
InterruptedException或TimeoutException(使用带超时的重载)
推荐始终使用带超时的版本,避免无限等待:
byte[] otherBuffer = exchanger.exchange(myBuffer, 3, TimeUnit.SECONDS);
常见错误:空指针、类型不匹配与线程生命周期错位
以下问题在实际调试中最常出现:
-
NullPointerException:传入null给exchange()(除非你明确允许 null 作为合法值,否则应提前校验) - 类型擦除导致的运行时 ClassCastException:两个线程使用不同泛型实例化同一个
Exchanger(例如一个用Exchanger<String>,另一个用原始类型Exchanger),编译期不报错,运行时交换后强转失败 - 线程提前退出或未启动:比如主线程启动两个子线程后立即结束,而子线程中的
exchange()永远等不到配对方——必须确保两个线程真正并发执行且都抵达交换点 - 重复调用未重置状态:
Exchanger本身无状态残留,但如果你复用同一对象做多次交换,需确保每次传入的数据是独立引用,避免因对象被对方修改导致逻辑混乱
性能与线程安全边界要注意什么
Exchanger 内部基于 CAS 和自旋优化,在低竞争下性能很好;但高频率、高延迟配对时,自旋可能浪费 CPU。这时可考虑:
- 控制交换频次(例如批量攒够 N 条记录再交换,而非每条都换)
- 使用带超时的
exchange(V x, long timeout, TimeUnit unit)避免单点卡死拖垮整体吞吐 - 不要在
synchronized块内调用exchange(),否则可能引发嵌套锁 + 阻塞,加剧死锁风险 -
Exchanger是线程安全的,但交换后的数据对象本身是否线程安全,由你负责——例如交换的是ArrayList,后续读写仍需同步
最易被忽略的一点:Exchanger 的配对是“严格一对一”的,没有队列、没有重试机制。如果业务逻辑中存在“可能只来一个线程”的情况,就别硬套它——改用 Phaser 或手动管理 CountDownLatch + 共享变量更稳妥。


















