从会读Exchanger这个单词到了解它

Exchanger的大解剖:

        这东西是一个数据交换器 线程在这个交换器里面进行数据的交换 大致如图

线程1的数据在交换器(Exchanger)里面等待线程2的数据到来进行数据的交换

交换的条件是成对的产生 同时可以有多对线程进行交换

        

代码演示

准备线程A

//线程A
class ExchangerA extends Thread{
    //保证同一个交换器
    private Exchanger<String> exchanger =null;

    public  ExchangerA (Exchanger exchanger){
        this.exchanger=exchanger;
    }
    public void run(){
        System.out.println("这里是A线程"+Thread.currentThread().getName());

        try {
            Thread.sleep(3000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("A的数据这边和B的数据准备进行交换");
        try {
            String s = exchanger.exchange("这是A的数据为11111");
            System.out.println("这是在A现场里交换回来B的信息为"+s);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("A和B交换完成继续干活");

    }
}

准备线程B

//线程B
class ExchangerB extends Thread{
    //保证同一个交换器
    private Exchanger<String> exchanger =null;

    public  ExchangerB (Exchanger exchanger) {
        this.exchanger = exchanger;
    }
    public void run(){

        System.out.println("这里是B线程"+Thread.currentThread().getName());
        try {
            Thread.sleep(3000l);

            System.out.println("B准备完成等待交换数据");

            String ss = exchanger.exchange("这是B的数据为222");
            System.out.println("这是在B线程里A的数据为"+ss);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("A和B数据交换完成进行接下来的工作");

    }


}

启动测试

public class ExchangerCS {

    public static void main(String[] args) {
        Exchanger<String> stringExchanger = new Exchanger<>();
        new ExchangerA(stringExchanger).start();
        new ExchangerB(stringExchanger).start();

    }


}

Exchanger中核心方法的源码分析

当我们在新建一个Exchanger的时候会在泛型声明类型 而这个泛型不能Exchanger之间交换数据的类型  它是我们给一个泛型给Exchanger 然后它把我们的类型统一用node节点来进行接收 交换也是node节点进行交换

 

item的位置是线程A数据放的地方 

match是未来进行和A线程交换的数据位置

parked是先到的线程在这里进行一个等待

Exchanger的核心方法 

exchanger.exchange

大致意思是是 当第一线程带数据进来了 会先进行判断是单槽位交换还是多槽位的交换

单槽位走 slotExchange

多槽位走arenaExchange

这里走单槽位方法

 private final Object slotExchange(Object item, boolean timed, long ns) {
//线程A先进来拿到当前带来的node1数据
        Node p = participant.get();
//获取到当前的线程A
        Thread t = Thread.currentThread();
//判断线程A状态是否中断
        if (t.isInterrupted()) // preserve interrupt status so caller can recheck
            return null;

        for (Node q;;) {
//判断单槽位节点的值是不是为空
            if ((q = slot) != null) {
                if (U.compareAndSwapObject(this, SLOT, q, null)) {
                    Object v = q.item;
//线程B的值就放到线程A的match里面
                    q.match = item;
                    Thread w = q.parked;
                    if (w != null)
//唤醒线程A
                        U.unpark(w);
                    return v;
                }
                // create arena on contention, but continue until slot null
                if (NCPU > 1 && bound == 0 &&
                    U.compareAndSwapInt(this, BOUND, 0, SEQ))
                    arena = new Node[(FULL + 2) << ASHIFT];
            }
            else if (arena != null)
                return null; // caller must reroute to arenaExchange
            else {
//代码逻辑先到这里把线程A带来的数据值给了node1里面的item里面
                p.item = item;
//这里让slot有了值 slot槽点就不为null
                if (U.compareAndSwapObject(this, SLOT, null, p))
                    break;
                p.item = null;
            }
        }

        // await release
//下面是拿到当前节点的hash值去计算一些条件
        int h = p.hash;
        long end = timed ? System.nanoTime() + ns : 0L;
        int spins = (NCPU > 1) ? SPINS : 1;
        Object v;
//优化的操作 不用太了解
        while ((v = p.match) == null) {
            if (spins > 0) {
                h ^= h << 1; h ^= h >>> 3; h ^= h << 10;
                if (h == 0)
                    h = SPINS | (int)t.getId();
                else if (h < 0 && (--spins & ((SPINS >>> 1) - 1)) == 0)
                    Thread.yield();
            }
//优化的操作 配对的线程到了还没有准备好 不用太了解
            else if (slot != p)
                spins = SPINS;
//
            else if (!t.isInterrupted() && arena == null &&
                     (!timed || (ns = end - System.nanoTime()) > 0L)) {
                U.putObject(t, BLOCKER, this);
//给这个node节点的park记下当前的线程
                p.parked = t;
                if (slot == p)
                    U.park(false, ns);
                p.parked = null;
                U.putObject(t, BLOCKER, null);
            }
            else if (U.compareAndSwapObject(this, SLOT, p, null)) {
                v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
                break;
            }
        }
        U.putOrderedObject(p, MATCH, null);
        p.item = null;
        p.hash = h;
        return v;
    }

简单的图解一下

最好还是在看看源码一步步的点进去看比较有用

P:线程进入Exchanger带来的node数据

slot:单槽位节点(一般第一个线程进入Exchanger这个值都为inull)

q:q类似于slot 会把slot进行替代 并把slot赋值为null

item:存线程带来的数据

Match:存交换来的数据

parked:存当前线程

源码中的一些条件判断 第一次线程进入不能生成 线程B进入会生成 最重要是自己debug看看大致流程

欢迎进行学习交流,不足之处请指出,喜欢麻烦点赞+收藏,谢谢各位大佬了

猜你喜欢

转载自blog.csdn.net/m0_67601895/article/details/129039160