OpenJDK8 TransferQueue内容

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

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!=null isData=true, item=null
消费者节点 isData=false, item=null isData=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 (
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值