exchanger是java中专为两个线程成对交换数据设计的同步工具,适用于双缓冲、流水线配对计算等严格双向协作场景,不适用于多线程、异步通知或单向传递。

Java 中线程间数据交换,Exchanger 是唯一专为此设计的同步工具 —— 但它只适用于「成对线程」严格交换的场景,用错地方反而会卡死。
什么时候该用 Exchanger?
它不是通用通信机制,而是为「两个线程协作完成一次数据交换」而生。典型场景包括:
- 双缓冲处理:线程 A 填充缓冲区 A,线程 B 处理缓冲区 B;填满后双方交换引用,A 开始填 B,B 开始处理 A
- 流水线式配对计算:如加密线程和解密线程轮流交换待处理块
- 测试中模拟双向握手:两个测试线程必须同步交换状态才能继续
如果涉及 >2 个线程、异步通知、或单向传递(比如生产者→消费者),别硬套 Exchanger —— 改用 BlockingQueue 或 TransferQueue 更合适。
Exchanger.exchange() 的阻塞逻辑和超时控制
调用 exchange(V x) 会阻塞,直到另一个线程也调用 exchange();两者配对成功后才各自返回对方传入的值。容易踩的坑:
- 只有一个线程调用,另一个没启动或已退出 → 当前线程永久阻塞(除非用带超时的重载)
- 两个线程都调用但传入
null→ 合法,但需确保业务逻辑能处理null值 - 超时版本
exchange(V x, long timeout, TimeUnit unit)在超时后抛出TimeoutException,不是返回null
示例(带超时防护):
Exchanger<string> ex = new Exchanger();
// 线程1
try {
String fromOther = ex.exchange("data-1", 3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
// 对方没来,主动放弃
}</string>
多个 Exchanger 实例是否线程安全?
Exchanger 实例本身是线程安全的,但它的核心约束在于「每次 exchange 只匹配最近一个未配对的调用」。这意味着:
- 同一个
Exchanger实例可被任意多线程共享,但只有成对调用才会成功交换 - 若线程 A 调用两次
exchange(),第一次会等线程 B,第二次则等线程 C(如果 B 已结束)—— 不保证与同一伙伴交换 - 没有内置“取消等待”机制;中断线程会导致
InterruptedException,但不会自动唤醒正在等待的另一方
所以实际使用中,建议绑定固定线程对(例如用 ThreadLocal<exchanger>></exchanger> 配合预分配),避免因线程复用导致配对混乱。
性能和 JVM 兼容性注意点
Exchanger 底层基于 CAS 和自旋优化,在 JDK 7+ 中表现稳定,但仍有细节要注意:
- JDK 8 引入了更高效的“多槽位”算法,减少高并发下争用;JDK 7 及以前在大量线程争抢时可能退化为全局锁
- 不要在
exchange()调用中执行耗时操作(如 I/O、复杂计算),否则会拖慢配对线程 - Android Runtime(ART)对
Exchanger支持有限,部分旧版本可能抛UnsupportedOperationException,需实测验证
真正难的从来不是怎么写那几行代码,而是判断「此刻到底需不需要交换」——多数所谓“线程间传数据”的需求,其实只需要一个 volatile 字段或 AtomicReference 就够了。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










