Exchanger详解

Java并发包(JUC)Exchanger详解

一、核心特性与定位

1.1 线程间数据交换器

Exchanger是Java并发包中实现线程间数据交换的核心工具类,允许两个线程在某个同步点交换对象。其核心设计理念基于同步数据交换模式,适用于需要双线程协作完成数据交换的场景。

类结构定位

1
1
1
Exchanger
Node
Objectitem
Threadparticipant

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 关键方法时序

ThreadA ThreadB Exchanger exchange(dataA) 阻塞等待 exchange(dataB) 阻塞等待 返回dataB 返回dataA ThreadA ThreadB Exchanger

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 性能瓶颈优化

诊断方法

  1. 使用JVisualVM监控线程状态
  2. 检测长时间exchange()的线程
  3. 分析交换频率

优化技巧

// 批量交换优化
List<DataChunk> chunks = new ArrayList<>(BATCH_SIZE);
for (int i = 0; i < BATCH_SIZE; i++) {
    chunks.add(processChunk());
}
List<DataChunk> received = exchanger.exchange(chunks); // 批量交换

六、源码关键逻辑

  1. 哈希码匹配

    • 通过随机生成的哈希码(ThreadLocalRandom)减少哈希冲突。
    • 冲突时重新生成哈希码,直到匹配成功。
  2. 阻塞与唤醒

    • 使用 UNSAFE.parkUNSAFE.unpark 实现线程阻塞和唤醒。
    • 依赖 AbstractQueuedSynchronizer 管理等待队列。
  3. 状态管理

    • 通过 Slot 类的 valueparkedThread 字段管理交换状态。
    • CAS 操作保证状态修改的原子性。

七、总结

Exchanger 通过哈希码匹配和 CAS 操作实现了高效的双向数据交换。其源码核心在于:

  • 交换槽管理:通过 Slot 类维护交换数据和线程状态。
  • 同步机制:基于 Parker 类(继承自 AQS)实现线程阻塞和唤醒。
  • 哈希冲突处理:通过随机哈希码和重试机制减少冲突。

理解其源码有助于在并发编程中灵活实现线程间协作,避免数据竞争和死锁问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值