exchanger是java并发包中专为两个线程成对协作设计的双向栅栏同步工具,核心机制为“同时到达、即时交换”,通过cas与内部插槽实现无锁配对,仅支持严格双向阻塞交换,不适用于多线程场景。

Exchanger 是 Java 并发包(java.util.concurrent)中一个专门用于**成对线程间安全交换对象**的同步工具。它不像 BlockingQueue 或 CountDownLatch 那样通用,而是聚焦于“两个线程在某个点相遇并互换数据”这一特定场景——比如生产者-消费者配对、工作线程协作处理缓冲区等。
Exchanger 的核心行为
它本质上是一个**双向栅栏(two-party barrier)**:调用 exchange(V x) 的两个线程会相互等待,直到都到达交换点,然后原子性地交换各自传入的对象,并各自获得对方的对象。如果只有一个线程调用,它会一直阻塞,直到另一个线程也调用;若超时或被中断,则抛出对应异常。
- 必须恰好两个线程参与一次交换,多于或少于都会导致阻塞(除非设了超时)
- 交换是原子的:不会出现“只拿到一半数据”或“中间状态可见”的问题
- 类型安全:泛型参数
V确保双方交换同类型对象(实际中常为同一类或接口)
典型使用方式:双缓冲数据交换
常见于需要交替填充和消费缓冲区的场景,比如图形渲染线程与计算线程协作:
Exchanger<byte> exchanger = new Exchanger();
byte[] bufferA = new byte[1024];
byte[] bufferB = new byte[1024];
// 线程1:填充缓冲区后交换
byte[] myBuf = bufferA;
while (running) {
fillData(myBuf); // 填充当前缓冲区
myBuf = exchanger.exchange(myBuf); // 交给对方,拿到对方填好的缓冲区
}
// 线程2:消费缓冲区后交换
byte[] myBuf = bufferB;
while (running) {
processData(myBuf); // 处理当前缓冲区
myBuf = exchanger.exchange(myBuf); // 交还空缓冲区,拿到新填充的
}</byte>
这样无需锁或 volatile,就实现了缓冲区的无竞争交接。
注意事项和陷阱
Exchanger 看似简单,但误用容易引发死锁或性能问题:
- 不要重复复用同一个 Exchanger 实例做不同语义的交换——比如线程 A 和 B 用它传数据,C 和 D 却用它传控制信号,可能因等待错配而永久阻塞
- 避免在 exchange() 调用前后持有其他锁,否则可能造成锁顺序不一致导致死锁
-
注意超时设置:可选的
exchange(V x, long timeout, TimeUnit unit)能防止一方崩溃后另一方无限等待 - 交换对象本身需线程安全:Exchanger 只保证交换动作原子,不保证你交换出去的对象内部状态是否可被并发访问
替代方案对比
不是所有“线程间传数据”都该用 Exchanger:
- 单向传递 → 优先考虑
BlockingQueue(如ArrayBlockingQueue)或SynchronousQueue - 多个线程协同 →
CyclicBarrier或Phaser更合适 - 只需通知+传参 →
CountDownLatch+ 成员变量 /AtomicReference更轻量
Exchanger 的价值在于“严格两人、即时交换、无中间存储”,用对了就很优雅,用错了反而增加复杂度。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











