Java并发包(JUC)Exchanger详解
一、核心特性与定位
1.1 线程间数据交换器
Exchanger是Java并发包中实现线程间数据交换的核心工具类,允许两个线程在某个同步点交换对象。其核心设计理念基于同步数据交换模式,适用于需要双线程协作完成数据交换的场景。
类结构定位:
1.2 核心特性矩阵
特性 | 行为表现 | 适用场景 |
---|---|---|
双向数据交换 | 支持两个线程交换对象 | 流水线处理、结果聚合 |
同步屏障 | 交换前双方线程阻塞 | 严格时序要求场景 |
超时机制 | exchange(timeout)支持 | 防止永久阻塞 |
类型安全 | 支持泛型参数 | 强类型数据交换 |
二、核心机制解析
2.0. 类结构
public class Exchanger<V> {
// 内部类:交换槽
private static final class Slot<V> {
V value;
Thread parkedThread; // 阻塞在此槽的线程
int hash; // 哈希码(用于匹配)
int bound; // 匹配范围
Slot() {
hash = ThreadLocalRandom.current().nextInt(); // 随机哈希码
}
}
// 内部类:同步器(基于 AQS)
private static final class Node {
final int bound;
final Object item;
volatile Node next;
Node(int bound, Object item) {
this.bound = bound;
this.item = item;
}
}
// 核心字段
private final Slot<V> slot; // 交换槽
private final Parker parker; // 同步器
// 构造器
public Exchanger() {
slot = new Slot<>();
parker = new Parker();
}
// 核心方法:交换数据(阻塞)
public V exchange(V x) throws InterruptedException {
Slot<V> s = slot;
Object v = x;
int m = 0; // 匹配模式
int h = s.hash; // 哈希码
Node node = null;
retry:
for (;;) {
if (m != 0 && h == s.hash && s.value != null) { // 匹配成功
V y = (V)s.value;
if (node != null) {
node.item = y; // 传递结果
node = null;
}
s.value = null;
parker.unpark(s.parkedThread); // 唤醒对方线程
return y;
}
if (s.parkedThread != null) { // 槽已被占用
if (node == null) {
node = new Node(h, v); // 创建新节点
h = ThreadLocalRandom.current().nextInt(); // 重新生成哈希码
}
if (parker.park(this, node)) { // 进入阻塞
if (node.item != null) { // 被唤醒后继续匹配
v = node.item;
node = null;
continue retry;
}
}
// 线程被中断或超时
if (node != null) {
node.item = null;
node = null;
}
throw new InterruptedException();
}
// 尝试占用槽
if (UNSAFE.compareAndSwapObject(s, valueOffset, null, v)) {
s.parkedThread = Thread.currentThread(); // 标记当前线程
if (parker.park(this, node)) { // 进入阻塞
if (node != null) {
v = (V)node.item; // 获取对方数据
node = null;
continue retry;
}
}
// 线程被中断或超时
UNSAFE.compareAndSwapObject(s, valueOffset, v, null);
s.parkedThread = null;
if (node != null) {
node.item = null;
node = null;
}
throw new InterruptedException();
}
// 哈希码冲突,重新生成
if (m == 0) {
h = ThreadLocalRandom.current().nextInt();
m = 1;
} else {
h = ThreadLocalRandom.current().nextInt();
m = 0;
}
}
}
// 同步器实现(基于 AQS)
private static final class Parker extends AbstractQueuedSynchronizer {
// 省略具体实现(依赖 AQS 的 park/unpark 机制)
}
}
2.0.1. 关键方法详解
-
exchange(V x)
:- 尝试将数据
x
放入交换槽。 - 如果对方线程已到达交换点,直接交换数据并返回对方数据。
- 否则,进入阻塞状态,直到对方线程调用
exchange()
。
- 尝试将数据
-
Parker.park(Object blocker, Object node)
:- 基于
UNSAFE.park
实现线程阻塞。 - 通过
AbstractQueuedSynchronizer
管理等待队列。
- 基于
-
Parker.unpark(Thread thread)
:- 唤醒指定线程,继续执行交换逻辑。
2.1 交换流程实现
内部数据结构:
// 简化版核心字段
private static class Node {
final Object item;
final Thread participant;
Node(Object item, Thread participant) {
this.item = item;
this.participant = participant;
}
}
private volatile Node slot;
private volatile boolean even;
2.2 关键方法时序
2.3 中断与超时机制
中断处理:
public V exchange(V x) throws InterruptedException {
Object v;
boolean interrupted = true;
try {
while ((v = doExchange(x, false, 0L)) == null) {
// 等待交换
}
interrupted = false;
return (V)v;
} finally {
if (interrupted)
Thread.currentThread().interrupt();
}
}
三、典型使用场景
3.1 流水线处理
图像处理流水线:
// 图像处理双缓冲
Exchanger<BufferedImage> imageExchanger = new Exchanger<>();
// 生产者线程
new Thread(() -> {
BufferedImage rawImage = captureFrame();
while (!Thread.currentThread().isInterrupted()) {
BufferedImage processed = process(rawImage);
rawImage = imageExchanger.exchange(processed); // 交换缓冲区
}
}).start();
// 消费者线程
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
BufferedImage image = imageExchanger.exchange(null);
display(image);
}
}).start();
3.2 遗传算法
种群交叉操作:
// 遗传算法个体交换
Exchanger<Individual> populationExchanger = new Exchanger<>();
// 父代选择线程
new Thread(() -> {
Population parents = selectParents(currentPopulation);
Population offspring = crossover(parents);
Population nextGen = populationExchanger.exchange(offspring); // 交换种群
currentPopulation = nextGen;
}).start();
// 环境评估线程
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
Population pop = populationExchanger.exchange(evaluate(currentPopulation));
// 更新评估结果
}
}).start();
3.3 结果聚合
分布式计算聚合:
// MapReduce结果聚合
Exchanger<Map<String, Integer>> resultExchanger = new Exchanger<>();
// Mapper线程
new Thread(() -> {
Map<String, Integer> localResults = map(rawData);
Map<String, Integer> globalResults = resultExchanger.exchange(localResults);
globalResults.putAll(localResults);
}).start();
// Reducer线程
new Thread(() -> {
Map<String, Integer> globalResults = new HashMap<>();
while (!globalResults.isEmpty()) {
Map<String, Integer> partial = resultExchanger.exchange(globalResults);
globalResults.putAll(partial);
}
}).start();
四、最佳实践
4.1 交换对象设计
不可变对象:
// 使用不可变对象保证线程安全
public final class ImmutableData {
private final int value;
private final String metadata;
public ImmutableData(int value, String metadata) {
this.value = value;
this.metadata = metadata;
}
// 省略getter方法
}
// 交换使用
Exchanger<ImmutableData> exchanger = new Exchanger<>();
data = exchanger.exchange(new ImmutableData(42, "result"));
4.2 超时控制模式
分级超时机制:
// 主超时控制
boolean exchanged = false;
try {
ImmutableData otherData = exchanger.exchange(myData, 30, TimeUnit.SECONDS);
exchanged = true;
process(otherData);
} catch (TimeoutException e) {
System.err.println("交换超时,执行回滚");
handleTimeout();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (!exchanged) {
cleanupResources();
}
}
4.3 异常处理
防御性编程:
// 交换时捕获异常
try {
Object received = exchanger.exchange(data);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
handleInterrupt();
} catch (BrokenBarrierException e) {
handleExchangerBreak();
}
五、常见问题与解决方案
5.1 交换器破裂
现象:
- 当某个线程被中断或超时,交换器会进入破裂状态
- 其他等待线程将收到BrokenBarrierException
解决方案:
// 防御性交换器使用
try {
Object result = exchanger.exchange(data);
} catch (BrokenBarrierException e) {
System.err.println("交换器破裂,执行恢复操作");
recoverFromExchangeBreak();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
5.2 线程中断处理
最佳实践:
// 正确处理中断
try {
exchanger.exchange(data);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
// 执行清理操作
handleInterrupt();
}
5.3 性能瓶颈优化
诊断方法:
- 使用JVisualVM监控线程状态
- 检测长时间exchange()的线程
- 分析交换频率
优化技巧:
// 批量交换优化
List<DataChunk> chunks = new ArrayList<>(BATCH_SIZE);
for (int i = 0; i < BATCH_SIZE; i++) {
chunks.add(processChunk());
}
List<DataChunk> received = exchanger.exchange(chunks); // 批量交换
六、源码关键逻辑
-
哈希码匹配:
- 通过随机生成的哈希码(
ThreadLocalRandom
)减少哈希冲突。 - 冲突时重新生成哈希码,直到匹配成功。
- 通过随机生成的哈希码(
-
阻塞与唤醒:
- 使用
UNSAFE.park
和UNSAFE.unpark
实现线程阻塞和唤醒。 - 依赖
AbstractQueuedSynchronizer
管理等待队列。
- 使用
-
状态管理:
- 通过
Slot
类的value
和parkedThread
字段管理交换状态。 CAS
操作保证状态修改的原子性。
- 通过
七、总结
Exchanger
通过哈希码匹配和 CAS
操作实现了高效的双向数据交换。其源码核心在于:
- 交换槽管理:通过
Slot
类维护交换数据和线程状态。 - 同步机制:基于
Parker
类(继承自AQS
)实现线程阻塞和唤醒。 - 哈希冲突处理:通过随机哈希码和重试机制减少冲突。
理解其源码有助于在并发编程中灵活实现线程间协作,避免数据竞争和死锁问题。