OpenJDK8 TransferQueue内容

本文详细介绍了Java中的TransferQueue接口及其在OpenJDK8中的实现,如LinkedTransferQueue和SynchronousQueue。TransferQueue不同于普通队列,它支持生产者消费者模式的高效传输。LinkedTransferQueue基于链表实现,而SynchronousQueue则提供了非公平和公平两种策略。文章深入探讨了这两个类的原理和源码分析。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

TransferQueue接口与常规的Queue实现不太一样,因此我把它单独拎出来做介绍。

1. 类结构图

在这里插入图片描述
图中的类/接口主要来自两个包。

java.util包

  1. Queue接口
  2. AbstractQueue抽象类

java.util.concurrent包(juc)

  1. BlockingQueue 阻塞队列接口
  2. TransferQueue 接口
  3. LinkedTransferQueue
  4. SynchronousQueue

其中,Queue、AbstractQueue、BlockingQueue在前文都有所提及,不再赘述。

2. TransferQueue(传输队列接口)

TransferQueue接口继承自BlockingQueue接口,因此有常规阻塞队列的所有操作。
区别于常规的阻塞队列,提供了一种适用于“生产者”、“消费者”模式的传输队列。考虑以下几个场景:

  1. 容器为空,消费者先到并因为当前无数据而阻塞,此时生产者到达并生产数据,从而消费者能消费数据,返回“传输成功(true)”;——tryTransfer(E e)
  2. 容器为空,生产者先到并尝试生产数据,但当前没有消费者阻塞等待数据,生产者立马放弃传输,返回“传输失败(false)”; ——tryTransfer(E e)
  3. 容器为空,此时生产者到达并尝试生产数据,由于当前没有消费者,生产者(有限时间内)阻塞。在有限时间内如果有消费者到达接收数据,返回“传输成功”,否则返回“传输失败”。——tryTransfer(E e, long timeout, TimeUnit unit)
  4. 容器为空,此时生产者到达并尝试生产数据,由于当前没有消费者,生产者(无限)阻塞,直到消费者到达并消费数据。 ——transfer(E e)

以上提到的容器都可以理解成队列,生产就是“入队操作”,消费就是“出队操作”。
本接口提供的方法主要就是针对以上几种场景,接口源码如下:

/**
 1. 一种生产者等待消费者接收元素的阻塞队列。
 2. tryTransfer(Object) 非阻塞
 3. tryTransfer(Object,long,TimeUnit) 有限时间阻塞
 4. transfer(Object) 无限阻塞
 5. @since 1.7
 */
public interface TransferQueue<E> extends BlockingQueue<E> {
    /**
     * 如果已经有一个消费者在等待接收,则立即传送给定的元素;否则,不传送元素,返回false
     */
    boolean tryTransfer(E e);

    /**
     * 传送元素到消费者,如果当前没有等待接收的消费者,无限等待
     */
    void transfer(E e) throws InterruptedException;
    
    /**
     * 如果给定时间内,有消费者等待接收元素,则传送元素并返回true;否则,不传送元素,返回false
     */
    boolean tryTransfer(E e, long timeout, TimeUnit unit) throws InterruptedException;
    
    /**
     * 判断是否“至少有一个消费者在等待接收元素”
     */
    boolean hasWaitingConsumer();
    
    /**
     * 获取当前正在等待元素的消费者数目
     */
    int getWaitingConsumerCount();
}

3. LinkedTransferQueue基于链表的传输队列

3.1 原理分析

基于链表实现传输队列。链表中的每个节点主要由以下属性组成:

	// 用作标记是否是生产者占用的数据(true),反之,为消费者线程占用,为false
    final boolean isData;
    // 如果是生产者占用(isData=true),表示实际数据;如果是消费者占用(isData=false),表示null
    // 通过CAS设置
    volatile Object item;
    // 下一个节点
    volatile Node next;
    // 等待的消费者线程,如果是生产者占用,为null
    volatile Thread waiter;

同时,节点有两种状态(“未匹配”和“已匹配”),对应的节点属性变化可以由下面的表格表示:

匹配前匹配后
生产者节点isData=true, item!=nullisData=true, item=null
消费者节点isData=false, item=nullisData=false, item=this

借助节点的类型,如果当前节点与队列中节点类型一致(同为生产/消费),则执行入队操作;如果节点类型不一致,则执行出队操作,更新队首位置。
在这里插入图片描述

附上参考资料:

  1. Java并发编程之LinkedTransferQueue阻塞队列详解
  2. 并发编程—— LinkedTransferQueue

3.2 源码

由于所有的队列操作都由xfer核心方法实现,这里我只贴xfer的代码分析

	/*
     * 核心xfer方法的how参数可能的取值
     */
    // 用于非阻塞的poll, tryTransfer
    private static final int NOW   = 0;
    // 用于offer、put、add插入操作(由于无界,所以都不会阻塞)
    private static final int ASYNC = 1;
    // 用于无限阻塞的transfer、take
    private static final int SYNC  = 2;
    // 用于有限时间阻塞的poll、tryTransfer
    private static final int TIMED = 3;

	/**
     * 核心方法,用于实现所有的队列方法
     * @param e 传输的数据或null值
     * @param haveData 是否插入类操作,如果true, 参数e不能为空
     * @param how 表示是哪类操作:NOW/ASYNC/SYNC/TIMED
     * @param nanos 超时时间(纳秒)
     */
    private E xfer(E e, boolean haveData, int how, long nanos) {
        // haveData 为true,表示是插入类操作,必须保证e不为空,否则抛NPE
        if (haveData && (e == null))
            throw new NullPointerException();
        // 将要添加的节点
        Node s = null;

        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)
                        break;
                    // 匹配成功
                    if (p.casItem(item, e)) {
                        for (Node q = p; q != h;) {
                            Node n = q.next;  // update by 2 unless singleton
                            // 更新head为匹配节点的next节点
                            if (head == h && casHead(h, n == null ? q : n)) {
                                // 将旧节点自连接
                                h.forgetNext();
                                break;
                            }                 // advance and retry
                            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
            }

            // 链表中所有节点无法匹配时会走到这里
            if (how != NOW) {
                // 新建节点s
                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
        }
    }

4. SynchronouseQueue同步队列

4.1 原理分析

SynchronouseQueue提供了两种策略方式:

  1. 非公平策略(默认) - 基于栈方式,LIFO后进先出。
  2. 公平策略 - 基于队列方式,FIFO先进先出。

SynchronouseQueue核心方法在于两个内部类对抽象方法 transfer(E e, boolean timed, long nanos)的实现。

原理部分我觉得自己无法说的比参考博客清楚,所以这里偷懒附上链接,请自行食用:

  1. SynchronousQueue原理解析
  2. jdk11源码–SynchronousQueue源码分析

4.2 源码

附上我本人在源码学习中的部分理解

/**
 * 一种阻塞队列实现
 * 无容量概念
 * 入队操作必须等待其它线程的出队操作,反之,出队操作也必须等待其它线程的入队操作
 * @since 1.5
 * @author Doug Lea and Bill Scherer and Michael Scott
 */
public class SynchronousQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
    
    /**
     * 栈/队列的通用内部api(栈:入栈/出栈;队列:入队/出队)
     * Shared internal API for dual stacks and queues.
     */
    abstract static class Transferer<E> {
        /**
         * 入队/出队操作。
         * 如果e为null,则表示无数据,数据从生产者传输给容器(队列)
         * 如果e不为null,则表示有数据。数据从容器(队列)传输给消费者
         */
        abstract E transfer(E e, boolean timed, long nanos);
    }

    // CPU核数
    static final int NCPUS = Runtime.getRuntime().availableProcessors();
    /**
     * 有限时间阻塞前最大自旋次数
     */
    static final int maxTimedSpins = (NCPUS < 2) ? 0 : 32;
    /**
     * 无限阻塞前最大的自旋次数
     */
    static final int maxUntimedSpins = maxTimedSpins * 16;

    /**
     * The number of nanoseconds for which it is faster to spin
     * rather than to use timed park. A rough estimate suffices.
     */
    static final long spinForTimeoutThreshold = 1000L;

    /** 非公平策略 - 栈方式 */
    static final class TransferStack<E> extends Transferer<E> {
    
        // 消费者占用节点标记
        static final int REQUEST    = 0;
        // 生产者占用节点标记
        static final int DATA       = 1;
        /** Node is fulfilling another unfulfilled DATA or REQUEST */
        static final int FULFILLING = 2;

        /** Returns true if m has fulfilling bit set. */
        static boolean isFulfilling(int m) { return (m & FULFILLING) != 0; }

        // 栈中的节点
        static final class SNode {
            // 下一个节点
            volatile SNode next;        // next node in stack
            // 与当前节点匹配的节点,阻塞时为null
            volatile SNode match;       // the node matched to this
            // 记录线程
            volatile Thread waiter;     // to control park/unpark
            // 数据
            Object item;                // data; or null for REQUESTs
            int mode;
            ...
        }

        // 栈顶元素
        volatile SNode head;
        ...

        /**
         * 插入/获取一个元素
         * 如果参数e为空,说明是消费者线程占用
         * 如果参数e不为空,说明是生产者线程占用
         */
        @SuppressWarnings("unchecked")
        E transfer(E e, boolean timed, long nanos) {
            /*
             * 循环完成下面三个操作之一:
             * 1. 如果当前栈为空,或者已经存在“同模式”(同属于生产者或消费者)的节点,则添加新节点
             * 2. 如果当前栈中包含与本次操作“互补模式”的节点,则先入栈,然后两个元素(分别为生产、消费,顺序可能调换)一同出栈,返回对应的数据项
             * 3. 如果当前栈顶是一个标记为“匹配”类型的节点,帮助它出栈。与2中出栈区别,此步骤并不返回对应的数据项
             */

            SNode s = null; // constructed/reused as needed
            // 模式(消费者REQUEST,生产者DATA)
            int mode = (e == null) ? REQUEST : DATA;

            for (;;) {
                // 栈顶节点
                SNode h = head;
                // 对应上面的操作1(栈为空或者“同模式”),需要添加新节点
                if (h == null || h.mode == mode) {
                    // 非阻塞方式
                    if (timed && nanos <= 0) {
                        // 如果栈顶元素已被取消,则执行出栈操作
                        if (h != null && h.isCancelled())
                            casHead(h, h.next);
                        else
                            return null;
                    } else if (casHead(h, s = snode(s, e, h, mode))) {  // CAS方式入栈一个新节点
                        // 有限时间内阻塞节点s,看是否有“互补模式”的节点
                        SNode m = awaitFulfill(s, timed, nanos);
                        // 如果阻塞时间超时,仍没有“互补”节点,则执行节点取消,返回null
                        if (m == s) {
                            clean(s);
                            return null;
                        }
                        // 走到这里,表示栈中的前两个元素完成了一次“匹配”
                        // 将栈顶前两元素出栈,并设置上新的栈顶
                        if ((h = head) != null && h.next == s)
                            casHead(h, s.next);
                        // 如果当前线程是REQUEST-消费模式,则从“互补”的m(生产模式)中获取数据;否则,s才是生产模式的数据
                        return (E) ((mode == REQUEST) ? m.item : s.item);
                    }
                } else if (!isFulfilling(h.mode)) { // 栈顶不是匹配状态的节点,且与当前操作“互补模式”
                    // 如果栈顶节点已经被“取消”,CAS执行出栈,栈指向下一元素
                    if (h.isCancelled())
                        casHead(h, h.next);
                    else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) { // 插入一个新的“互补模式”节点
                        // 循环直到节点匹配或者与s匹配的节点消失
                        for (;;) {
                            // 正常情况下,m就是s节点的匹配节点
                            SNode m = s.next;
                            // 如果m为空,表明栈中已经没有“互补模式”的节点,那么直接将栈顶指向null,表示空栈
                            if (m == null) {
                                casHead(s, null);
                                s = null;
                                break;
                            }
                            SNode mn = m.next;
                            // 节点s与节点m“互补模式”达到匹配,此时栈中同时将两者出栈,并将栈顶指向后续节点
                            if (m.tryMatch(s)) {
                                casHead(s, mn);
                                // 如果当前线程是REQUEST-消费模式,则从“互补”的m(生产模式)中获取数据;否则,s才是生产模式的数据
                                return (E) ((mode == REQUEST) ? m.item : s.item);
                            } else                  // lost match
                                s.casNext(m, mn);   // help unlink
                        }
                    }
                } else {// 对应上面描述中的操作3. 这里栈顶已经是匹配状态的节点
                    // 正常m就是栈顶h节点的“互补”节点
                    SNode m = h.next;
                    // 但是如果h是栈中仅有的匹配状态元素,栈中没有别的元素了,那么直接将栈顶指向null,表示空栈
                    if (m == null)
                        casHead(h, null);
                    else {
                        SNode mn = m.next;
                        // 协助完成“匹配”操作,并设置新的栈顶
                        if (m.tryMatch(h))
                            casHead(h, mn);
                        else
                            h.casNext(m, mn);
                    }
                }
            }
        }

        /**
         * 线程自旋/阻塞,直到节点s完成匹配或者设置时间到期
         */
        SNode awaitFulfill(SNode s, boolean timed, long nanos) {
            // 设置过期的时间点,0表示不阻塞方式
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            Thread w = Thread.currentThread();
            int spins = (shouldSpin(s) ?
                         (timed ? maxTimedSpins : maxUntimedSpins) : 0);
            for (;;) {
                // 当前线程发生中断,对插入的节点执行“取消”操作
                if (w.isInterrupted())
                    s.tryCancel();
                SNode m = s.match;
                // 节点s拿到了“互补模式”的节点m,直接返回
                if (m != null)
                    return m;
                // 有限时间阻塞(这里兼容了非阻塞方式)
                if (timed) {
                    // 阻塞倒计时
                    nanos = deadline - System.nanoTime();
                    if (nanos <= 0L) {
                        // 线程阻塞已经超时,对插入的节点执行“取消”操作
                        s.tryCancel();
                        continue;
                    }
                }
                // 自旋操作
                if (spins > 0)
                    spins = shouldSpin(s) ? (spins-1) : 0;
                // 如果节点线程为null,设置上线程
                else if (s.waiter == null)
                    s.waiter = w; // establish waiter so can park next iter
                // 无限阻塞
                else if (!timed)
                    LockSupport.park(this);
                else if (nanos > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanos);
            }
        }

        ...
    }

    /** 公平策略 - 队列方式 */
    static final class TransferQueue<E> extends Transferer<E> {
        /** Node class for TransferQueue. */
        static final class QNode {
            // 队列下一节点
            volatile QNode next;          // next node in queue
            // 数据
            volatile Object item;         // CAS'ed to or from null
            // 节点的线程
            volatile Thread waiter;       // to control park/unpark
            // 标记是否数据节点(true表示生产者占用节点,false表示消费者占用节点)
            final boolean isData;
			...
        }

        // 队列头
        transient volatile QNode head;
        // 队列尾部
        transient volatile QNode tail;
        /**
         * 处于“取消”的节点
         */
        transient volatile QNode cleanMe;

        TransferQueue() {
            // 用虚节点作为队首
            QNode h = new QNode(null, false); // initialize to dummy node.
            head = h;
            tail = h;
        }
        
        ...
        
        /**
         * 插入/获取一个数据
         */
        @SuppressWarnings("unchecked")
        E transfer(E e, boolean timed, long nanos) {
            /*
             * 循环完成以下两种行为之一:
             * 1. 如果队列为空,或者已经存在“同模式”(同属于生产者或消费者)的节点,则往队列中添加新节点
             * 2. 如果队列中有“互补模式”的阻塞节点,CAS匹配并执行出队
             */

            QNode s = null; // constructed/reused as needed
            // 如果为true,表示是“生产者线程”;如果是false,表示是“消费者线程”
            boolean isData = (e != null);

            for (;;) {
                QNode t = tail;
                QNode h = head;
                if (t == null || h == null)         // saw uninitialized value
                    continue;                       // spin

                // 对应情况1,需要进行入队操作
                if (h == t || t.isData == isData) {
                    QNode tn = t.next;
                    if (t != tail)                  // inconsistent read
                        continue;
                    // 队尾的next不为空,CAS方式更新队尾指针
                    if (tn != null) {
                        advanceTail(t, tn);
                        continue;
                    }
                    // 非阻塞方式,直接返回null,而且不用执行节点入队
                    if (timed && nanos <= 0)
                        return null;
                    // 新建节点s
                    if (s == null)
                        s = new QNode(e, isData);
                    // CAS方式设置队尾节点的next为节点s
                    if (!t.casNext(null, s))        // failed to link in
                        continue;

                    // CAS更新队尾指针到节点s
                    advanceTail(t, s);
                    // 线程阻塞,等待“匹配”
                    Object x = awaitFulfill(s, e, timed, nanos);
                    // 节点s“取消”
                    if (x == s) {                   // wait was cancelled
                        clean(t, s);
                        return null;
                    }

                    if (!s.isOffList()) {           // not already unlinked
                        advanceHead(t, s);          // unlink if head
                        if (x != null)              // and forget fields
                            s.item = s;
                        s.waiter = null;
                    }
                    return (x != null) ? (E)x : e;

                } else { // 对应情况2
                    // 最先匹配的队首节点
                    QNode m = h.next;
                    if (t != tail || m == null || h != head)
                        continue;                   // inconsistent read

                    Object x = m.item;
                    // 如果队首元素已经是“匹配/取消”状态,挪动队首指针到m(即原队首h出队),并重复循环
                    if (isData == (x != null) ||
                        x == m ||
                        !m.casItem(x, e)) {
                        advanceHead(h, m);
                        continue;
                    }

                    // 成功匹配,执行h节点出队,队首指向下一个节点m
                    advanceHead(h, m);
                    LockSupport.unpark(m.waiter);
                    return (x != null) ? (E)x : e;
                }
            }
        }
    }

    private transient volatile Transferer<E> transferer;

    /**
     * 构造SynchronousQueue,默认非公平方式
     */
    public SynchronousQueue() {
        this(false);
    }

    /**
     * 根据给定的公平策略,构造SynchronousQueue
     * 公平策略,基于TransferQueue
     * 非公平策略,基于TransferStack
     */
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }

	...
}
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值