【Java并发编程实战】——LinkedTransferQueue

LinkedTransferQueue 一个基于链表的无边界阻塞队列。这个队列按照生产者FIFO(先进先出)的顺序提供元素。队列的头节点是等待时间最长的生产者,队尾是等待时间最短的生产者。它有个特殊的方法 transfer,若调用时有消费者等待,则直接匹配将元素转移给消费者,若没有消费者等待则自旋后阻塞。

执行流程图
在这里插入图片描述
内部结构

public class LinkedTransferQueue<E>{
    /** head of the queue; null until first enqueue */
    transient volatile Node head;

    /** tail of the queue; null until first append */
    private transient volatile Node tail;
   
    /**
     * Queue nodes. Uses Object, not E, for items to allow forgetting
     * them after use.  Relies heavily on Unsafe mechanics to minimize
     * unnecessary ordering constraints: Writes that are intrinsically
     * ordered wrt other accesses or CASes use simple relaxed forms.
     */
    //链表节点,存储元素和阻塞的消费者
    static final class Node {
    	//初始化的时候,若是消费者则为false,生产者为true
        final boolean isData;   // false if this is a request node
        //初始化的时候如果是生产者不会为空,之后用于匹配节点
        volatile Object item;   // initially non-null if isData; CASed to match
        volatile Node next;
        //若等待中的线程需要被阻塞,则存储阻塞的线程
        volatile Thread waiter; // null until waiting
    }
}

内部的入队列和出队列调用的都是同一个方法 xfer(),通过一个特殊的转换标识来区分,下面的注释很清楚了,搞清楚每个方法对应的特殊值。

/*
 * Possible values for "how" argument in xfer method.
 */
private static final int NOW   = 0; // for untimed poll, tryTransfer
private static final int ASYNC = 1; // for offer, put, add
private static final int SYNC  = 2; // for transfer, take
private static final int TIMED = 3; // for timed poll, tryTransfer

transfer 方法,若有消费者等待直接将元素转移给消费者。

/**
 * Transfers the element to a consumer, waiting if necessary to do so.
 *
 * <p>More precisely, transfers the specified element immediately
 * if there exists a consumer already waiting to receive it (in
 * {@link #take} or timed {@link #poll(long,TimeUnit) poll}),
 * else inserts the specified element at the tail of this queue
 * and waits until the element is received by a consumer.
 *
 * @throws NullPointerException if the specified element is null
 */
public void transfer(E e) throws InterruptedException {
    if (xfer(e, true, SYNC, 0) != null) {
        Thread.interrupted(); // failure possible only due to interrupt
        throw new InterruptedException();
    }
}

public boolean tryTransfer(E e) {
    return xfer(e, true, NOW, 0) == null;
}

public boolean tryTransfer(E e, long timeout, TimeUnit unit)
    throws InterruptedException {
    if (xfer(e, true, TIMED, unit.toNanos(timeout)) == null)
        return true;
    if (!Thread.interrupted())
        return false;
    throw new InterruptedException();
}

获取元素

public E take() throws InterruptedException {
    E e = xfer(null, false, SYNC, 0);
    if (e != null)
        return e;
    Thread.interrupted();
    throw new InterruptedException();
}

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    E e = xfer(null, false, TIMED, unit.toNanos(timeout));
    if (e != null || !Thread.interrupted())
        return e;
    throw new InterruptedException();
}

public E poll() {
    return xfer(null, false, NOW, 0);
}

重点看下转移方法 xfer

/**
 * Implements all queuing methods. See above for explanation.
 *
 * @param e the item or null for take
 * @param haveData true if this is a put, else a take
 * @param how NOW, ASYNC, SYNC, or TIMED
 * @param nanos timeout in nanosecs, used only if mode is TIMED
 * @return an item if matched, else e
 * @throws NullPointerException if haveData mode but e is null
 */
private E xfer(E e, boolean haveData, int how, long nanos) {
    if (haveData && (e == null))
    	//生产者进来,元素不能为空
        throw new NullPointerException();
    //若需要,为当前进来的线程创建一个节点,插入到队列
    Node s = null;                        // the node to append, if needed

    retry:
    for (;;) {                            // restart on append race
		//找第一个节点,不匹配继续向下找,直到下一个节点为空退出
        for (Node h = head, p = h; p != null;) { // find & match first node
            boolean isData = p.isData;
            Object item = p.item;
          	//节点被取消或者已被转移就不匹配
            if (item != p && (item != null) == isData) { // unmatched
            	//看当前线程是否和节点的操作类型一致
                if (isData == haveData)   // can't match
                	//一致的话退出,看是否需要加入队尾
                    break;
                //操作不一致代表匹配上了,执行交换元素
                if (p.casItem(item, e)) { // match
                	//为了节省 CAS 操作的开销,节点完成转换后,不会立即更新头节点
                    for (Node q = p; q != h;) {
                        Node n = q.next;  // update by 2 unless singleton
                        //>=2个节点匹配了,才更新第三个节点为头节点
                        if (head == h && casHead(h, n == null ? q : n)) {
                            h.forgetNext();
                            break;
                        }                 // advance and retry
                        //CAS失败
                        if ((h = head)   == null ||
                            (q = h.next) == null || !q.isMatched())
                        	//若现在的头节点的下一个节点没有匹配成功,不更新头节点
                            break;        // unless slack < 2
                    }
                    //唤醒等待的线程
                    LockSupport.unpark(p.waiter);
                    return LinkedTransferQueue.<E>cast(item);
                }
            }
            Node n = p.next;
            //节点不匹配,往后移
            p = (p != n) ? n : (h = head); // Use head if p offlist
        }
		//没有限制时间的 poll、tryTransfer 会直接退出,其他的方法需要加入队尾
        if (how != NOW) {                 // No matches available
            if (s == null)
            	//构建一个新节点
                s = new Node(e, haveData);
            //尝试加入到队尾
            Node pred = tryAppend(s, haveData);
            if (pred == null)
            	//前驱节点为空代表入队失败,重头再来一遍
                continue retry;           // lost race vs opposite mode
            if (how != ASYNC)
            	//需要等待被匹配
                return awaitMatch(s, pred, e, (how == TIMED), nanos);
        }
        return e; // not waiting
    }
}

加入队尾

/**
 * Tries to append node s as tail.
 *
 * @param s the node to append
 * @param haveData true if appending in data mode
 * @return null on failure due to losing race with append in
 * different mode, else s's predecessor, or s itself if no
 * predecessor
 */
private Node tryAppend(Node s, boolean haveData) {
    for (Node t = tail, p = t;;) {        // move p to last node and append
        Node n, u;                        // temps for reads of next & tail
        if (p == null && (p = head) == null) {
        	//队列为空,初始化
            if (casHead(null, s))
                return s;                 // initialize
        }
        //检查尾部节点能否作为前驱
        else if (p.cannotPrecede(haveData))
            return null;                  // lost race vs opposite mode
        //尾节点变动,向下遍历,找到最后的节点,继续循环
        else if ((n = p.next) != null)    // not last; keep traversing
            p = p != t && t != (u = tail) ? (t = u) : // stale tail
                (p != n) ? n : null;      // restart if off list
        //设置自己为尾节点
        else if (!p.casNext(null, s))
        	//CAS失败,找到最后的节点,继续循环
            p = p.next;                   // re-read on CAS failure
        else {
            if (p != t) {                 // update if slack now >= 2
            	//和设置头节点一样,不立即修改尾节点
                while ((tail != t || !casTail(t, s)) &&
                       (t = tail)   != null &&
                       (s = t.next) != null && // advance and retry
                       (s = s.next) != null && s != t);
            }
            //返回前驱
            return p;
        }
    }
}

/**
 * Returns true if a node with the given mode cannot be
 * appended to this node because this node is unmatched and
 * has opposite data mode.
 */
final boolean cannotPrecede(boolean haveData) {
    boolean d = isData;
    Object x;
    //操作类型和s不匹配,并且没有转移成功,表明尾节点不能作为前驱
    return d != haveData && (x = item) != this && (x != null) == d;
}

等待被匹配
若是方法 transfer, take timed 为 false
若是方法 timed poll, tryTransfer timed 为 true

/**
 * Spins/yields/blocks until node s is matched or caller gives up.
 *
 * @param s the waiting node
 * @param pred the predecessor of s, or s itself if it has no
 * predecessor, or null if unknown (the null case does not occur
 * in any current calls but may in possible future extensions)
 * @param e the comparison value for checking match
 * @param timed if true, wait only until timeout elapses
 * @param nanos timeout in nanosecs, used only if timed is true
 * @return matched item, or e if unmatched on interrupt or timeout
 */
private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    Thread w = Thread.currentThread();
    int spins = -1; // initialized after first item and cancel checks
    ThreadLocalRandom randomYields = null; // bound if needed

    for (;;) {
        Object item = s.item;
        if (item != e) {                  // matched
            // assert item != s;
            //s节点已经被转换了,清除s
            s.forgetContents();           // avoid garbage
            //阻塞的线程被相反的操作唤醒后,在这里退出
            return LinkedTransferQueue.<E>cast(item);
        }
        //线程被中断,或者任务已超时
        if ((w.isInterrupted() || (timed && nanos <= 0)) &&
                s.casItem(e, s)) {        // cancel
            //从队列中移除
            unsplice(pred, s);
            //被中断或者超时自动唤醒,在这里退出
            return e;
        }
		//计算需要自旋的次数
        if (spins < 0) {                  // establish spins at/near front
            if ((spins = spinsFor(pred, s.isData)) > 0)
                randomYields = ThreadLocalRandom.current();
        }
        else if (spins > 0) {             // spin
            --spins;
            if (randomYields.nextInt(CHAINED_SPINS) == 0)
                Thread.yield();           // occasionally yield
        }
        else if (s.waiter == null) {
        	//自旋达到最大限制仍不能匹配成功
            s.waiter = w;                 // request unpark then recheck
        }
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos > 0L)
            	//设置了超时时间,但还没有超时,阻塞剩余 nanos 时间
                LockSupport.parkNanos(this, nanos);
        }
        else {
        	//transfer, take 操作直接阻塞,等待被唤醒
            LockSupport.park(this);
        }
    }
}

LinkedTransferQueue 可以看做是 LinkedBlockingQueue 和 SynchronousQueue 的结合
LinkedBlockingQueue 能够存储元素,但是内部使用了大量的锁,并发效率不高。
SynchronousQueue 内部不能存储元素,可能会导致大量生产者阻塞。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值