阻塞队列-SynchronousQueue

SynchronousQueue

基本介绍

  1. SynchronousQueue和其它阻塞队列最大的区别就是,SynchronousQueue存储数据的方式不同,它将数据封装在Node中,另外还存储了线程信息
  2. 从SynchronousQueue存储消息的特点可以决定,线程在存储或者取数据的时候,线程必须等待消息匹配成功才能做其它的事情。
  3. SynchronousQueue提供了两种Node存储数据,一种是基于队列的结构,遵循先进先出的原则;另一种是基于栈的结构,先进后出
  4. SynchronousQueue的核心功能是去匹配生产者和消费者,消费者Node直接从消费者Node中取数据
  5. SynchronousQueue保证线程安全是基于CAS操作完成的,并没有使用Lock锁,因此性能比其它阻塞队列更高
  6. 常用的方法(添加数据):
  7. offer():生产者在添加数据时,如果有消费者在等待消息,则匹配成功,即添加数据成功;如果没有消费者等待消息,则匹配失败,即添加失败。
  8. offer(time,unit):生产者在添加数据时阻塞一段时间,在指定时间内:如果有消费者在等待消息,则匹配成功,即添加数据成功;如果没有消费者等待消息,则匹配失败,即添加失败。
  9. put():生产者在添加数据时,如果有消费者等待消息,直接匹配,添加成功;如果没有消费者等待消息,则一直等待,直到有消费者消费消息。
  10. 获取数据的方法和添加数据方法相同

基本使用

构造方法中可以传入一个boolean值,如果为true使用队列是数据结构,如果为false默认使用栈的数据结构,无参构造默认false

public class SynchronousQueueTest {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<String> queue = new SynchronousQueue<>(true);
//        SynchronousQueue<String> queue = new SynchronousQueue<>();

        for (int i = 0; i < 3; i++) {
            int finalI = i;
            Thread.sleep(10);
            new Thread(() -> {
                try {
                    queue.put("生产者线程" + finalI);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }

        Thread.sleep(10);

        for (int i = 0; i < 3; i++) {
            int finalI = i;
            Thread.sleep(10);
            new Thread(() -> {
                try {
                    System.out.println("消费者线程" + finalI +  "获取消息:" + queue.take());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }).start();
        }

    }
}

使用栈的数据结构结果:

消费者线程0获取消息:生产者线程2
消费者线程1获取消息:生产者线程1
消费者线程2获取消息:生产者线程0

使用队列的数据结果:

消费者线程0获取消息:生产者线程0
消费者线程1获取消息:生产者线程1
消费者线程2获取消息:生产者线程2

从结果可以看出,栈结构后插入的数据被先消费;队列结构先添加的数据被先消费

源码分析

成员变量

只需关心transferer变量,所有的存取数据的方法最终都是调用了transferer对象的transfer方法

//这个变量是指定存储的数据机构是基于队列存储还是基于栈存储
 private transient volatile Transferer<E> transferer;

构造方法

  //调用有参构造默认传入false
    public SynchronousQueue() {
        this(false);
    }
    //如果为true基于队列存储,如果false基于栈存储,默认使用栈存储
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }

抽象静态内部类Transferer

只定义了一个抽象方法transfer

    abstract static class Transferer<E> {
    /**
     * 
     * @param e 存储的元素,如果为消费者则传入null
     * @param timed 是否阻塞等待
     * @param nanos 阻塞时间的纳秒值
     * @return
     */
        abstract E transfer(E e, boolean timed, long nanos);
    }

TransferQueue实现类

基于队列存储,遵循先进出原则

 static final class TransferQueue<E> extends Transferer<E> {
    //队列的头节点
    transient volatile QNode head;
    //队列的尾节点
    transient volatile QNode tail;
    //静态内部类,存储数据的Node
    static final class QNode {
        // 下一个node节点,可以看出是单向链表实现队列
        volatile QNode next; 
        // 要存取的元素
        volatile Object item;
        // 存取数据的线程
        volatile Thread waiter; 
        //标记是否是存数据
        final boolean isData;
    }
    //构造方法,可以看出构建了一个尾节点
    TransferQueue() {
        QNode h = new QNode(null, false); // initialize to dummy node.
        head = h;
        tail = h;
    }
    //这个方法是基于队列形式存储数据的逻辑实现
    E transfer(E e, boolean timed, long nanos) {
        //省略……
    }
}

TransferStack实现类

基于栈存储,遵循先进后出原则

  static final class TransferStack<E> extends Transferer<E> {
    static final class SNode {
        //当前节点下个节点的node
        volatile SNode next;
        //和当前节点可以匹配的生产者节点或消费者节点
        volatile SNode match;     
        //当前线程
        volatile Thread waiter;   
        //存储的数据,如果为消费者则为null
        Object item;                
        int mode;
    }
    //============只有一个默认的无参构造=======

    //这个方法是基于栈形式存储数据的逻辑实现
    E transfer(E e, boolean timed, long nanos) {
        //………………
    }
}

分析TransferQueue的transfer方法

    /**
    * e:元素内容,如果消费者,则为null
    * timed:是否阻塞等待
    * nanos:等待时间的纳秒值 
    */
    E transfer(E e, boolean timed, long nanos) {
            QNode s = null; 
            //判断是否是生产者
            boolean isData = (e != null);
            //死循环
            for (;;) {
                //拿到队列的尾节点和头节点
                QNode t = tail;
                QNode h = head;
                //防止对象还没创建好的校验
                if (t == null || h == null)         // saw uninitialized value
                    continue;                       // spin
                //头尾节点相等或者头节点和当前节都是生产者或都是消费者,则进行队列插入操纵
                if (h == t || t.isData == isData) { // empty or same-mode
                    //获取头节点的下一个节点
                    QNode tn = t.next;
                    //头节点发生变化,证明有其它线程改变了队列,下次for循环
                    if (t != tail)                  // inconsistent read
                        continue;
                    //头节点的下一个节点存在了,证明有其它线程改变队列了,将tail节点替换为tn,下次for循环
                    if (tn != null) {               // lagging tail
                        advanceTail(t, tn);
                        continue;
                    }
                    //返回结果1 -> 如果需要不需要等待或等待时间到了,返回
                    if (timed && nanos <= 0L)       // can't wait
                        return null;
                    //如果可以阻塞且没有创建过节点对象,这里为了for循环只创建一次
                    if (s == null)
                        s = new QNode(e, isData);
                    //基于CAS将tail节点的next设置为当前线程节点,等待生产者生产数据或等待被消费,如果设置失败下次for循环
                    if (!t.casNext(null, s))        // failed to link in
                        continue;
                    //CAS插入成功,替换tail指向
                    advanceTail(t, s);              // swing tail and wait
                    //如果插入队列中了,挂起线程,等待消费或被消费
                    Object x = awaitFulfill(s, e, timed, nanos);
                    //如果节点取消,清空当前节点,将引用断开
                    if (x == s) {                   // wait was cancelled
                        clean(t, s);
                        return null;
                    }
                    //判断当前节点是否还在队列中,
                    if (!s.isOffList()) {           // not already unlinked
                        //将当前节点设置为head
                        advanceHead(t, s);          // unlink if head
                        if (x != null)              // and forget fields
                            //将当前节点item设置为自己
                            s.item = s;
                        //线程置为null
                        s.waiter = null;
                    }
                    //生产者返回当前节点元素,消费者返回匹配节点中的元素
                    return (x != null) ? (E)x : e;

                } 
                //证明可以匹配
                else {                            // complementary-mode
                    //拿到head的next作为要匹配的节点
                    QNode m = h.next;               // node to fulfill
                    //tail节点变动或者head的下个节点为null或者head节点变动,证明并发了,下次for循环
                    if (t != tail || m == null || h != head)
                        continue;                   // inconsistent read
                    //如果没有并发问题,拿到nead的next节点中的元素
                    Object x = m.item;
                    //isData == (x != null)判断是否都是生产者或都是消费者,x == m是否取消节点,!m.casItem(x, e)表示交互数据失败------->并发校验
                    if (isData == (x != null) ||x == m ||!m.casItem(x, e)) {  
                        //重新设置head节点,下次for循环
                        advanceHead(h, m);          
                        continue;
                    }
                    //如果没有并发问题,替换head
                    advanceHead(h, m);              // successfully fulfilled
                    //唤醒head的next节点中的线程
                    LockSupport.unpark(m.waiter);
                    //生产者返回当前节点元素,消费者返回匹配节点中的元素
                    return (x != null) ? (E)x : e;
                }
            }
        }

原理图

请添加图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值