阻塞队列SynchronousQueue

1 SynchronousQueue简述

这个队列中,不存储数据,而是存储生产者或者是消费者,当存储一个生产者到SynchronousQueue后,生产者会阻塞(具体根据调用的方法),生产者最终会有如下结果:

  • 如果在阻塞期间有消费者来匹配,生产者就会将绑定的消息交给消费者

  • 生产者等到阻塞结果,或者不允许阻塞,直接失败

  • 生产者在阻塞期间线程被中断了,直接告辞

消费者与生产者的原理一样

生产者:

 

消费者:

方法同上

示例代码:

public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<String> synchronousQueue = new SynchronousQueue<String>();
        String msg ="你好!";
        // 生产者
        new Thread(()->{
            boolean b = false;
            try {
                b = synchronousQueue.offer(msg,1, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println(b);
        }).start();

        Thread.sleep(100);
        // 消费者
        new Thread(()->{
            System.out.println(synchronousQueue.poll());
        }).start();

    }

核心内部类:

 abstract static class Transferer<E> {
       // 这个方法就是生产者和消费者在调用读写数据时用到的核心方法
        abstract E transfer(E e, boolean timed, long nanos);
    }

生产者在调用上述方法时,第一个参数传递e,消费者在调用时,第一个参数传递null

Transferer存在两种实现方式:

  • TransferStack

  • TransferQueue

在构建SynchronousQueue时会指定使用哪种子类实现

public SynchronousQueue() {
    this(false);
}

public SynchronousQueue(boolean fair) {
    // 如果是公平,使用TransferQueue(队列结构,FIFO先进先出)
    // 如果是非公平,使用TransferStack(栈结构,先进后出)
    transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}

2 TransferQueue的源码分析

构建TransferQueue对象

TransferQueue() {
    // 构建一个头尾节点都指向伪节点的队列,并设置isData属性为false
    QNode h = new QNode(null, false); // initialize to dummy node.
    head = h;
    tail = h;
}

QNode(Object item, boolean isData) {
    this.item = item;
    // 区分消费者还是生产者
    this.isData = isData;
}

put方法源码分析

public void put(E e) throws InterruptedException {
    // 非空判断  抛出异常
    if (e == null) throw new NullPointerException();
    // 如果为null,则是由于线程中断导致的,直接抛出异常
    if (transferer.transfer(e, false, 0) == null) {
        Thread.interrupted();
        throw new InterruptedException();
    }
}

offer方法源码分析

public boolean offer(E e) {
    // 非空判断  抛出异常
    if (e == null) throw new NullPointerException();
    // 返回false,表示线程中断
    return transferer.transfer(e, true, 0) != null;
}

offer(E e, long timeout, TimeUnit unit) 源码分析

public boolean offer(E e, long timeout, TimeUnit unit)
    throws InterruptedException {
    // 非空判断  抛出异常
    if (e == null) throw new NullPointerException();
    // 如果为null,则是由于超时或线程中断导致的,否则直接返回true
    if (transferer.transfer(e, true, unit.toNanos(timeout)) != null)
        return true;
    // 查看线程的中断标记位,如果不是中断,就是超时,返回false,否则抛出中断异常
    if (!Thread.interrupted())
        return false;
    throw new InterruptedException();
}

transfer核心方法源码分析

// 核心方法实现
E transfer(E e, boolean timed, long nanos) {
    // 构造生产者 或消费者对象
    QNode s = null; // constructed/reused as needed
    // true 代表生产者  否则代表消费者
    boolean isData = (e != null);
    // 因为没有加锁的操作,使用死循环 走大量的判断来避免并发问题
    for (;;) {
        // 将头尾节点的成员变量赋值给局部变量
        QNode t = tail;
        QNode h = head;
        // 做了一个健壮性判断,如果为null 则说明TransferQueue没有初始化(可能发生了指令重排)
        if (t == null || h == null)         // saw uninitialized value
            continue;  
        // 头节点等于尾节点:整个队列没有生产者也没有消费者
        // 如果有节点,同时当前节点和队列节点属于同一种角色
        // 以上两个情况均进入队列                   
        if (h == t || t.isData == isData) {
            // 拿到头节点的下一个节点
            QNode tn = t.next;
            //头节点不等于尾节点,说明有并发问题,重新走for循环
            if (t != tail)                  // inconsistent read
                continue;
            // 头节点的后置节点不为null,说明有线程并发,添加了一个节点
            if (tn != null) {               // lagging tail
                // 直接帮助那个线程修改tail指针指向,并继续执行for循环
                advanceTail(t, tn);
                continue;
            }
            // timed :false  无线阻塞,true 阻塞一段时间
            // 如果timed为true  并且阻塞时间小于等于0,则返回null
            if (timed && nanos <= 0L)       // can't wait
                return null;
            // 如果可以阻塞,则将当前元素e构建为QNode节点,设置为生产者
            if (s == null)
                s = new QNode(e, isData);
            // 基于CAS操作,将tail节点的next从null设置为当前的QNode
            if (!t.casNext(null, s))        // failed to link in
                // 如果进来,说明修改失败,重新执行for训话
                continue;
            // CAS操作成功,替换tail的指向
            advanceTail(t, s);              // swing tail and wait
            // 如果进到队列了,则挂起线程,等生产者 或消费者
            // x是返回替换后的数据
            Object x = awaitFulfill(s, e, timed, nanos);

            // 说明节点取消了
            if (x == s) {                   // wait was cancelled
                // 清空当前节点,跳过当前节点
                clean(t, s);
                return null;
            }
            // 判断当前节点是否还在队列中
            if (!s.isOffList()) {  
                // 将当前节点设置为head         
                advanceHead(t, s);          // unlink if head
                   // 如果拿到了数据,说明我是消费者
                if (x != null)              // and forget fields
                    // 将当前节点的item设置为自己
                    s.item = s;
                // 置空线程
                s.waiter = null;
            }
            return (x != null) ? (E)x : e;

        } else {  
            // 匹配队列中的角色
            // head的next作为要匹配的角色                          // complementary-mode
            QNode m = h.next;               // node to fulfill
            // 做并发判断,如果头节点、头节点的next节点、尾节点发生变化,则重新执行for循环
            if (t != tail || m == null || h != head)
                continue;                   // inconsistent read
            // 没有并发问题,可以拿数据了
            Object x = m.item;
            //如果满足(isData == (x != null),说明出现了并发问题,消费者去匹配消费者,这不合理
            if (isData == (x != null) ||    
                // 说明节点取消了,自己指向了自己
                x == m || 
                // 如果以上都不满足,说明可以CAS交换数据了
                // 如果交换失败了,说明有并发问题
                // 此时需要重新设置head节点,继续执行for循环
                !m.casItem(x, e)) {         // lost CAS
                advanceHead(h, m);          // dequeue and retry
                continue;
            }
            // 替换head
            advanceHead(h, m);              
            // 唤醒head.next中的线程
            LockSupport.unpark(m.waiter);
            // 匹配好了,数据也交换了,直接返回
            // 如果x不等于null,说明队列中是生产者,当前是消费者,直接返回x具体数据;
            // 否则是队列是消费者,当前是生产者,直接返回自己的数据
            return (x != null) ? (E)x : e;
        }
    }
}

transfer方法流程图

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值