Exchanger 是 Java 并发包(java.util.concurrent)中用于两个线程间双向数据交换的同步工具类。它通过提供线程间的配对点(Rendezvous Point),实现双方在同步点交换数据。以下从核心原理到实践场景进行全面解析:
🔧 一、核心原理与设计
1. 基本机制
- 同步点模型:
- 线程调用
exchange(V x)时被阻塞,直到另一个线程也调用该方法。 - 双方到达后,互换数据对象并继续执行。
- 线程调用
- 底层实现:
- 槽位(Slot):通过
AtomicReference存储线程的Node节点,CAS 操作实现线程匹配。 - Arena 模式:高并发时升级为多槽位数组,避免单槽位竞争(如
Node[] arena)。 - 线程控制:阻塞使用
LockSupport.park(),唤醒由配对线程触发LockSupport.unpark()。
- 槽位(Slot):通过
2. 关键特性
- 双向交换:数据可双向传递(生产者→消费者,消费者→生产者)。
- 线程阻塞:未配对时线程无限期阻塞(支持超时控制)。
- 泛型支持:交换对象类型由泛型
V定义(如Exchanger<String>)。
3. 数据交换流程
sequenceDiagram
participant A as Thread A
participant E as Exchanger
participant B as Thread B
A->>E: exchange(dataA)
B->>E: exchange(dataB)
E-->>A: dataB
E-->>B: dataA
🛠️ 二、使用方法与示例
1. 基础用法
import java.util.concurrent.Exchanger;
public class BasicExchangeExample {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
String dataA = "Data from Thread-A";
String received = exchanger.exchange(dataA); // 阻塞等待交换
System.out.println("Thread-A received: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
new Thread(() -> {
try {
String dataB = "Data from Thread-B";
String received = exchanger.exchange(dataB);
System.out.println("Thread-B received: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
输出:
Thread-A received: Data from Thread-B
Thread-B received: Data from Thread-A
2. 超时控制
try {
String result = exchanger.exchange(data, 2, TimeUnit.SECONDS); // 超时2秒
} catch (TimeoutException e) {
System.out.println("交换超时,执行备用逻辑");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
3. 缓冲区交换(生产者-消费者)
Exchanger<List<String>> exchanger = new Exchanger<>();
// 生产者线程
new Thread(() -> {
List<String> buffer = new ArrayList<>();
while (true) {
fillBuffer(buffer); // 生产数据
buffer = exchanger.exchange(buffer); // 交换空缓冲区
}
}).start();
// 消费者线程
new Thread(() -> {
List<String> buffer = Collections.emptyList();
while (true) {
buffer = exchanger.exchange(buffer); // 交换填充后的缓冲区
processBuffer(buffer); // 消费数据
buffer.clear();
}
}).start();
优势:避免创建新对象,减少 GC 压力。
⚖️ 三、优缺点分析
| 维度 | 优点 | 缺点 |
|---|---|---|
| 性能 | 无锁交换(CAS+自旋),高吞吐(Arena 模式)。 | 仅支持两个线程,多线程需分组+多个实例。 |
| 资源开销 | 直接交换数据,无中间队列内存消耗。 | 线程阻塞可能造成延迟(尤其未配对时)。 |
| 数据一致性 | 交换后双方获得对方最新数据。 | 对象共享时需注意线程安全(非深拷贝)。 |
| 灵活性 | 支持超时、中断控制。 | 无法升级锁(如读锁→写锁)。 |
🎯 四、适用场景
1. 理想场景
| 场景 | 说明 |
|---|---|
| 双缓冲流水线 | 生产者与消费者直接交换缓冲区(如视频帧处理)。 |
| 遗传算法 | 染色体交叉配对时交换基因数据。 |
| 校对系统 | 双人录入数据后交换比对结果(如财务校对)。 |
| 双向通信协议 | 客户端与服务端交替发送/接收消息(如自定义RPC)。 |
2. 不适用场景
- 多线程交换(>2个线程):需改用
CyclicBarrier或BlockingQueue。 - 高频写操作:频繁交换导致线程阻塞,性能低于
ConcurrentLinkedQueue。
⚠️ 五、注意事项与最佳实践
- 避免死锁:
- 使用带超时的
exchange(),防止线程永久阻塞。 - 确保两个线程成对调用(奇数线程会导致剩余线程饥饿)。
- 使用带超时的
- 对象线程安全:
- 交换可变对象时,需同步控制(如
synchronized)避免并发修改。
- 交换可变对象时,需同步控制(如
- 性能调优:
- 大数据交换使用缓冲区重用(减少 GC)。
- 高并发时监控 Arena 层级(
Node.index)避免槽位竞争。
- 替代方案:
- 多线程场景:使用
SynchronousQueue(单向传输)或Phaser(多阶段同步)。
- 多线程场景:使用
💎 总结
Exchanger 是 Java 并发编程中轻量级双向数据交换的利器,核心价值在于:
- 简化协作:无需中间队列,直接实现线程间数据互换。
- 高效同步:CAS + Arena 机制支持高吞吐场景。
- 资源优化:缓冲区复用减少 GC 压力。
✨ 最佳实践:在严格双线程协作且读多写少的场景中优先使用(如流水线、校对系统),结合超时控制与对象安全设计,可显著提升系统健壮性。多线程需求需转向分组 Exchanger 或其他同步工具。
1174

被折叠的 条评论
为什么被折叠?



