Exchanger源码解析

Exchanger(交换者)是一个用于线程间协作的工具类。Exchanger 用于进行线程间的数据交换。它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据。这两个线程通过exchange 方法交换数据,如果第一个线程先执行 exchange() 方法,它会一直等待第二个线程也执行 exchange 方法,当两个线程都到达同步点时,这两个线程就可以交换数据,将本线程生产出来的数据传递给对方。

// 用于左移Node数组下标,以便得出数组在内存中偏移量来获取数据,避免伪共享
private static final int ASHIFT = 7;

// Node数组最大下标
private static final int MMASK = 0xff;

// 用于递增bound,每次递增一个SEQ
private static final int SEQ = MMASK + 1;

// CPU核心数
private static final int NCPU = Runtime.getRuntime().availableProcessors();

// 当前数组最大下标
static final int FULL = (NCPU >= (MMASK << 1)) ? MMASK : NCPU >>> 1;

// 自旋次数,CPU核心为1时自旋会被禁用
private static final int SPINS = 1 << 10;

// 用于exchange方法中参数为null时传递给其它线程的对象
private static final Object NULL_ITEM = new Object();

// 用于超时传递的对象
private static final Object TIMED_OUT = new Object();

// 节点用于保持需要交换的数据
@sun.misc.Contended static final class Node {
    int index;              // arana数组下标,多槽位时使用
    int bound;              // 上一次记录的bound 
    int collides;           // CAS失败次数
    int hash;               // 用于自旋的伪随机数
    Object item;            // 当前线程需要交换的数据
    volatile Object match;  // 匹配线程交换的数据
    volatile Thread parked; // 记录当前挂起的线程
}

// 用户记录线程状态的内部类
static final class Participant extends ThreadLocal<Node> {
    public Node initialValue() { return new Node(); }
}

// 记录线程状态
private final Participant participant;

// 多槽位数据交换使用
private volatile Node[] arena;

// 用于交换数据的槽位
private volatile Node slot;

/**
 * The index of the largest valid arena position, OR'ed with SEQ
 * number in high bits, incremented on each update.  The initial
 * update from 0 to SEQ is used to ensure that the arena array is
 * constructed only once.
 */
private volatile int bound;

exchange 方法

public V exchange(V x) throws InterruptedException {
    Object v;
    Object item = (x == null) ? NULL_ITEM : x; // translate null args
    if ((arena != null ||
         (v = slotExchange(item, false, 0L)) == null) &&
        ((Thread.interrupted() || // disambiguates null return
          (v = arenaExchange(item, false, 0L)) == null)))
        throw new InterruptedException();
    return (v == NULL_ITEM) ? null : (V)v;
}
private final Object slotExchange(Object item, boolean timed, long ns) {
    Node p = participant.get(); // 获取当前节点对象
    Thread t = Thread.currentThread(); // 当前线程
    // 线程中断,直接返回null
    if (t.isInterrupted()) // preserve interrupt status so caller can recheck
        return null;
    // 自旋
    for (Node q;;) {
        // 槽位slot不为null,则说明已经存在线程等待交换数据
        if ((q = slot) != null) {
            // CAS置空槽位slot
            if (U.compareAndSwapObject(this, SLOT, q, null)) {
                Object v = q.item; // 获取槽位中需要交换的对象
                q.match = item; // 将当前需要交换的数据设置到match中
                Thread w = q.parked; // 获取被挂起线程
                // 存在挂起线程,则唤醒
                if (w != null)
                    U.unpark(w); 
                return v; // 返回交换后的数据
            }
            // 存在竞争,其它线程抢先一步,需要使用多槽位交换方式
            // CPU为多核心 且 bound等于0(arana数组未初始化),则CAS操作将bound增加SEQ
            if (NCPU > 1 && bound == 0 &&
                U.compareAndSwapInt(this, BOUND, 0, SEQ))
                arena = new Node[(FULL + 2) << ASHIFT]; // 初始化arana数组
        }
        else if (arena != null) // 多槽位不为空,执行多槽位交换
            return null; // caller must reroute to arenaExchange
        else {
            // 表示当前线程是第一个线程进来交换数据 或 之前交换任务已完成,可重新认为是第一个线程,
            // 将需要交换的数据存放到槽位slot的item属性
            p.item = item;
            // CAS设置槽位为p
            if (U.compareAndSwapObject(this, SLOT, null, p))
                break; // CAS操作成功结束自旋
            p.item = null; // CAS设置槽位失败,置空item,继续自旋操作
        }
    }

    // 当前线程已经占据槽位,等待其它线程交换数据
    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(); // 线程让步,提供CPU利用率
        }
        // 存在线程交换数据,已修改槽位slot,但未修改match属性,则等待
        else if (slot != p)
            spins = SPINS;
        // 线程未中断 且 不是多槽位交换 且 (没有设置超时 或 超时时间未到)
        else if (!t.isInterrupted() && arena == null &&
                 (!timed || (ns = end - System.nanoTime()) > 0L)) {
            U.putObject(t, BLOCKER, this); // 设置线程t被当前对象阻塞
            p.parked = t; // 设置节点挂起线程属性parked
            // 如果槽位slot不等于null,表明还没有线程与之交换数据,则将当前线程挂起
            if (slot == p) 
                U.park(false, ns);
            p.parked = null; // 线程被唤醒,将节点挂起线程parked属性设置为null
            U.putObject(t, BLOCKER, null); // 设置线程t没有被任何对象阻塞
        }
        // 不满足上述添加,交换失败,重置槽位slot
        else if (U.compareAndSwapObject(this, SLOT, p, null)) {
            v = timed && ns <= 0L && !t.isInterrupted() ? TIMED_OUT : null;
            break;
        }
    }
    U.putOrderedObject(p, MATCH, null); // 置空match
    p.item = null; // 置空item
    p.hash = h;
    return v; // 返回交换数据
}

猜你喜欢

转载自blog.csdn.net/laravelshao/article/details/83000833