exchanger 是 java 并发包中用于两个线程间原子交换数据的专用同步工具,支持多轮配对、阻塞等待、类型安全及双缓冲高效复用,需避免多线程参与、提前退出和耗时操作。

Exchanger 是 Java 并发包(java.util.concurrent)中一个轻量、专用的线程间协作工具,适用于严格配对的两个线程之间同步交换数据的场景。它不适用于多线程广播或任意线程间通信,但一旦匹配成功,交换是原子且阻塞的——双方线程会彼此等待,直到对方也调用 exchange(),然后一次性完成数据互换。
Exchanger 的核心行为:成对阻塞与原子交换
每个 Exchanger 实例维护一个“交换点”,只有两个线程同时到达时才会完成交换;第三个线程调用会阻塞,直到前一对中的某个线程再次参与下一轮(即 Exchanger 支持多轮配对,但每轮仅限两人)。交换过程天然线程安全,无需额外加锁。
- 若线程 A 调用
exchanger.exchange(dataA),它会阻塞,直到另一个线程 B 也调用exchanger.exchange(dataB) - 一旦配对成功,A 得到
dataB,B 得到dataA,两者同时解除阻塞 - 如果某一线程被中断,另一方会抛出
InterruptedException,交换失败
典型使用场景:双缓冲数据处理
最常见的是生产者-消费者模型的变体——比如图形渲染中前后帧缓冲区切换、传感器数据采集与处理流水线等。两个线程交替填充与消费同一类资源,通过 Exchanger 高效交接。
- 线程 1(采集端)准备完一批数据后,调用
exchanger.exchange(buffer),把满缓冲区交给处理端,同时拿到空缓冲区继续采集 - 线程 2(处理端)在上一轮拿到满缓冲区后完成处理,再调用
exchanger.exchange(emptyBuffer),归还空缓冲区并获取新数据 - 全程无对象创建、无队列竞争,内存复用率高,延迟可控
使用时需注意的关键细节
Exchanger 简洁但有隐含约束,忽略易引发死锁或逻辑错乱:
-
必须恰好两个线程参与一轮交换:不能期望三个线程轮流交换;若某线程提前退出(如异常终止),另一方将永久阻塞,建议配合超时版本
exchange(V x, long timeout, TimeUnit unit) -
泛型类型需一致:声明为
Exchanger<string></string>时,两端传入/接收的都应是String,类型不匹配会在编译期报错 - 避免在 exchange() 中执行耗时操作:因为交换本身是同步点,长时间占用会导致另一方空等;应在 exchange 前准备好数据,exchange 后再做后续处理
- 不要重复使用已中断的 Exchanger 实例:中断发生后状态可能不可靠,建议每次交换失败后重建新实例或明确重置逻辑
一个可运行的双缓冲示例
以下代码模拟两个线程交替交换字符串缓冲区:
Exchanger<string> exchanger = new Exchanger();
Thread t1 = new Thread(() -> {
try {
String data = "from-t1";
String received = exchanger.exchange(data);
System.out.println("T1 sent: " + data + ", received: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread t2 = new Thread(() -> {
try {
String data = "from-t2";
String received = exchanger.exchange(data);
System.out.println("T2 sent: " + data + ", received: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
t1.start(); t2.start();
</string>
输出类似:
T1 sent: from-t1, received: from-t2
T2 sent: from-t2, received: from-t1
两行输出顺序不定,但必然成对出现,体现交换的同步性与原子性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











