Exchanger 是 Java 并发包中用于两个线程在同步点原子交换数据的协作工具;它通过成对阻塞机制实现双向交换,支持超时控制和重复使用,适用于双阶段计算、乒乓缓冲等场景。
exchanger 是 java 并发包(java.util.concurrent)中一个轻量级的线程间协作工具,专用于**成对线程在某个同步点交换数据**。它不适用于多线程广播或任意数量线程通信,而是精准匹配两个线程:当一个线程调用 exchange(v x) 时,它会阻塞,直到另一个线程也调用 exchange,双方才原子性地交换各自传入的对象。
核心机制:成对阻塞与原子交换
Exchanger 的本质是“等待配对”——它内部维护一个等待槽(slot),第一个调用 exchange() 的线程将数据放入并挂起;第二个线程到来后,两者数据立即互换,各自继续执行。若超时未等到配对线程,可抛出 TimeoutException(使用带超时的重载方法)。
- 只支持严格两个线程参与一次交换;第三个线程调用会一直阻塞,除非前一对已完成且新配对开始
- 交换是双向且原子的:线程 A 交出 dataA,拿到 dataB;线程 B 交出 dataB,拿到 dataA
- 线程可重复使用同一个 Exchanger 实例进行多次交换(如流水线式协作)
典型使用场景:双阶段计算协同
常见于需要“分而治之再合并”的双线程任务,例如:
- 一个线程预处理输入 A,另一个预处理输入 B;然后交换中间结果,各自基于对方结果完成后续计算
- 模拟生产者-消费者中的“乒乓缓冲”:线程1填充 bufferA,线程2填充 bufferB;填满后交换缓冲区指针,实现零拷贝切换
- 并行算法中的阶段性结果对齐(如某些迭代收敛计算中,两线程需同步中间状态)
代码示例:双线程交换中间计算结果
以下是一个完整可运行示例,模拟两个线程分别计算数组部分和,然后交换结果以完成交叉验证:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExchangerExample {
static class Worker implements Runnable {
private final String name;
private final int[] data;
private final Exchanger<Integer> exchanger;
private Integer localSum = 0;
Worker(String name, int[] data, Exchanger<Integer> exchanger) {
this.name = name;
this.data = data;
this.exchanger = exchanger;
}
@Override
public void run() {
// 阶段1:本地计算
for (int i : data) localSum += i;
System.out.println(name + " 计算本地和: " + localSum);
try {
// 阶段2:在同步点交换结果
Integer otherSum = exchanger.exchange(localSum);
System.out.println(name + " 收到对方和: " + otherSum + ",总和为: " + (localSum + otherSum));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println(name + " 被中断");
}
}
}
public static void main(String[] args) {
Exchanger<Integer> exchanger = new Exchanger<>();
int[] part1 = {1, 2, 3, 4};
int[] part2 = {5, 6, 7, 8};
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(new Worker("线程-A", part1, exchanger));
pool.submit(new Worker("线程-B", part2, exchanger));
pool.shutdown();
}
}
输出类似:
立即学习“Java免费学习笔记(深入)”;
线程-A 计算本地和: 10线程-B 计算本地和: 26
线程-A 收到对方和: 26,总和为: 36
线程-B 收到对方和: 10,总和为: 36
注意事项与最佳实践
-
避免单方面调用:确保两个线程确实会进入
exchange(),否则一个线程永久阻塞 -
注意线程生命周期:若某线程提前退出(如异常),另一线程将永远等待;建议配合超时版本
exchange(V x, long timeout, TimeUnit unit) - 类型安全:Exchanger 是泛型类,交换对象类型必须一致,编译期强制校验
-
不替代更通用工具:如需多线程协调,应考虑
CyclicBarrier或Phaser;如需单向传递,用BlockingQueue更合适

















