exchanger 是 java 并发包中专为两个线程双向交换数据设计的同步工具,核心是严格两两配对、原子性交换、不排队等待,适用于双缓冲、请求-响应等场景,需避免单线程调用或混用实例。

Exchanger 是 Java 并发包中专为两个线程成对协作设计的同步工具,不是用来协调多个线程,也不是简单传递信号——它的本质是在汇合点双向交换数据。用对了,代码更简洁、逻辑更清晰;用错了,容易卡死或超时失败。
Exchanger 的核心行为:配对即交换
它不维护队列,也不排队等待。一个线程调用 exchange() 后,会一直阻塞,直到另一个且仅另一个线程也调用同一个 Exchanger 实例的 exchange()。此时两者瞬间完成数据互换,各自拿到对方传来的对象,然后继续执行。
- 必须是严格两两配对:第三个线程调用会再次等待,形成新配对,旧等待不会累积
- 交换是原子性操作:双方数据同时交付,不存在“一方已交、一方未收”的中间态
- 泛型类型一致:声明为
Exchanger<string></string>,就不能一个传byte[],一个传Integer
两种 exchange 方法的实际选择
是否加超时,取决于你的场景对“等待容忍度”的判断:
-
exchange(V x):适合确定有搭档、且不能无限等的场景(如双缓冲渲染),但必须配合中断处理,否则可能永久挂起 -
exchange(V x, long timeout, TimeUnit unit):推荐用于不确定对方何时就绪的环境(如测试、异步回调、网络连接建立),超时后抛TimeoutException,可主动清理资源或重试
例如,在 Jetty 的 EndPoint 测试中,用 Exchanger 替代 CountDownLatch + SocketHolder,就能把“等 accept 完再传 socket”这个动作压缩成一行交换,避免手动同步和包装类。
典型适用场景与避坑提醒
它不是万能通信工具,而是为特定协作模式而生:
-
双缓冲切换:渲染线程填满后台 buffer,调用
exchanger.exchange(backBuffer);显示线程拿到该 buffer 进行绘制,同时把自己的空 buffer 交出去 - 请求-响应配对:线程 A 发请求字符串,线程 B 收到后处理并返回结果,双方在 exchange 点完成“发请求/收结果”和“收请求/发结果”两个动作
-
避免常见错误:不要在单线程里反复调用 exchange(没搭档,永远卡住);不要跨多个 Exchanger 实例混用;注意捕获
InterruptedException并恢复中断状态
简单可靠的数据交换写法
一个可直接运行的最小示例,体现关键结构:
Exchanger<string> ex = new Exchanger();
new Thread(() -> {
try {
String res = ex.exchange("Hello from A", 2, TimeUnit.SECONDS);
System.out.println("A got: " + res);
} catch (TimeoutException e) {
System.out.println("A timed out");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
new Thread(() -> {
try {
String res = ex.exchange("Hi from B");
System.out.println("B got: " + res);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();</string>
这里 A 设了 2 秒超时,B 不设——只要 B 在 2 秒内到达,交换成功;否则 A 报超时,B 则继续等待(直到被中断或程序结束)。











