SynchronousQueue、LinkedBlockingQueue、ArrayBlockingQueue三种队列的简单介绍

其实不光只有这三种,只是因为java提供的四种线程池用到了这三种队列,所以这里探究一下这三种队列的区别,如果看其他的队列,可以直接看java.util.Queue这个接口谁继承了,自己去研究去

文章目录

  • 0、BlockingQueue
  • 1、ArrayBlockingQueue
    • (1)ArrayBlockingQueue中基础的字段
    • (2)看一下poll方法
    • (3)看一下put方法
    • (4)结论
  • 2、LinkedBlockingQueue
    • (1)先看node的结构
    • (2)看队列的put方法
    • (3)看队列的poll方法(take自己查)
  • 3、SynchronousQueue(特殊的链表队列)
    • (1)、TransferQueue(先进先出)
      • 1)node的数据结构
      • 2) transfer的实现
    • (2)、TransferStack (后进先出)
      • 1)node数据结构
      • 2) transfer的实现逻辑

0、BlockingQueue

为什么先介绍这个呢?
后面的SynchronousQueueLinkedBlockingQueueDelayedWorkQueue都是实现的BlockingQueue接口

首先通过命名知道这是一个队列,常用的队列是下面两种

  1. 先进先出(FIFO):先插入队列的元素先出去
  2. 后进先出(LIFO): 后插入队列的元素先出去

之后再看什么事阻塞队列,通过下面的场景来模拟阻塞队列
在多线程的场景下,

  • 插入数据的速度明显比取出数据的速度快,那会造成队列积压的数据越来越多!
  • 取出数据的速度比插入的数据快,是不是造成取数的操作空转!

阻塞队列则是把当出现上面这种情况时把插入/取数的线程挂起,并且挂起和唤醒的前置条件不同,

  • 对于插入数据的线程而言,当队列中没有空位置时就挂起,有则唤醒
  • 对于取数的线程而言,当队列中没有数据时挂起,有数据则唤醒

下面是BlockingQueue接口中方法,下面接口的注释我都用谷歌翻译结合语境翻译过来了

public interface BlockingQueue<E> extends Queue<E> {
   /**
   如果不超过队列限制,立即将指定的元素插入此队列,成功时返回true,
   如果当前没有可用空间,则抛出 IllegalStateException。
   当使用容量受限的队列时,通常最好使用 {@link #offer(Object) offer}。
   */
    boolean add(E e);

   /**
   如果不超过队列限制,立即将指定的元素插入此队列,成功时返回  true,
   如果当前没有可用空间,则返回 false。
   当使用容量受限的队列时,这种方法通常比 {@link #add} 更可取,后者只能通过抛出异常而无法插入元素。
   */
    boolean offer(E e);

  /**
  	将指定元素插入此队列,如有必要等待空间可用。
  */
    void put(E e) throws InterruptedException;
	
   /**
   如果队列中还有空间,则将指定元素插入此队列,否则等待指定的等待时间
   */
    boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException;

   /**
   检索并删除此队列的头部,如有必要,请等待直到元素可用。
   */
    E take() throws InterruptedException;

  	/**
  	检索并删除此队列的头部,如果需要,则等待指定的等待时间以使元素可用。
  	*/
    E poll(long timeout, TimeUnit unit)
        throws InterruptedException;
    /**
    返回此队列理想情况下(在没有内存或资源限制的情况下)在没有阻塞的情况下可以接受的附加元素的数量,如果没有内在限制,则返回 {@code Integer.MAX_VALUE}。
  请注意,您不能总是通过检查 {@code remainingCapacity}来判断插入元素的尝试是否会成功,因为可能是另一个线程即将插入或删除一个元素。
    */    
    int remainingCapacity();
	/**
	从此队列中删除指定元素的单个实例,如果它存在。更正式地说,如果此队列包含一个或多个这样的元素,则删除一个元素 {@code e},例如 {@code o.equals(e)}。
	如果此队列包含指定的元素(或等效地,如果此队列因调用而更改),则返回 {@code true}。
	*/
    boolean remove(Object o);
  /**
  如果此队列包含指定元素,则返回 {@code true}。更正式地说,返回 {@code true} 当且仅当此队列包含至少一个元素 {@code e} 使得 {@code o.equals(e)}
  */
    public boolean contains(Object o);
	/**
	从此队列中删除所有可用元素并将它们添加到给定集合中。
	此操作可能比重复轮询此队列更有效。
	尝试将元素添加到集合 {@code c} 时遇到的失败可能导致元素不在任何集合中,当相关异常被抛出时,任何一个或两个集合中。
	尝试将队列排空到自身会导致{@code IllegalArgumentException}。此外,如果指定的集合在操作进行时被修改,则此操作的行为是未定义的。
	*/
    int drainTo(Collection<? super E> c);
	/**
	从这个队列中删除最多给定数量的可用元素并将它们添加到给定集合中。
	尝试将元素添加到 集合 {@code c} 时遇到的失败可能导致元素不在任何集合中,当相关异常被抛出时,任何一个或两个集合中。
	尝试将队列排空到自身会导致 {@code IllegalArgumentException}。此外,如果指定的集合在操作进行时被修改,则此操作的行为是未定义的。
	*/
    int drainTo(Collection<? super E> c, int maxElements);
}

上面的只是接口,实际上的运用还是需要用BlockingQueue的实现类,比如后面三个都是BlockingQueue的实现类

1、ArrayBlockingQueue

一种有缓冲的数组阻塞队列,必须设置队列的长度,因为涉及到缓存数组的长度

(1)ArrayBlockingQueue中基础的字段

/** The queued items */
    final Object[] items;

    /** items index for next take, poll, peek or remove */
    int takeIndex;

    /** items index for next put, offer, or add */
    int putIndex;

    /** Number of elements in the queue */
    int count;

    /*
     * Concurrency control uses the classic two-condition algorithm
     * found in any textbook.
     */

    /** Main lock guarding all access 锁*/
    final ReentrantLock lock;

(2)看一下poll方法

  public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : dequeue();
        } finally {
            lock.unlock();
        }
    }
     /**
     * Extracts element at current take position, advances, and signals.
     * Call only when holding lock.
     */
    private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

(3)看一下put方法

  /**
     * Inserts the specified element at the tail of this queue, waiting
     * for space to become available if the queue is full.
     *
     * @throws InterruptedException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public void put(E e) throws InterruptedException {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }
     /**
     * Inserts element at current put position, advances, and signals.
     * Call only when holding lock.
     */
    private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal();
    }

(4)结论

1、ArrayBlockingQueue队列内部是用数组当存储结构的,
2、有指针字段指向即将要put的索引位置和poll的索引位置
3、数组是循环使用的,不会每次取数或存数后把数组从头开始排列
4、取数和存数有锁字段来保证

2、LinkedBlockingQueue

一种有缓冲的链表阻塞队列,可以设置队列缓冲的长度,默认Integer.MAX _VALUE,实际上因为是链表,长度可以认为是无界

(1)先看node的结构

就是正常的一个node节点结构

  /**
     * Linked list node class
     */
    static class Node<E> {
        E item;

        /**
         * One of:
         * - the real successor Node
         * - this Node, meaning the successor is head.next
         * - null, meaning there is no successor (this is the last node)
         */
        Node<E> next;

        Node(E x) { item = x; }
    }

(2)看队列的put方法

/**
     * Inserts the specified element at the tail of this queue, waiting if
     * necessary for space to become available.
     *
     * @throws InterruptedException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        // Note: convention in all put/take/etc is to preset local var
        // holding count negative to indicate failure unless set.
        int c = -1;
        Node<E> node = new Node<E>(e);
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            /*
             * Note that count is used in wait guard even though it is
             * not protected by lock. This works because count can
             * only decrease at this point (all other puts are shut
             * out by lock), and we (or some other waiting put) are
             * signalled if it ever changes from capacity. Similarly
             * for all other uses of count in other wait guards.
             */
             //不是通过加锁而是判断队列是否满了
            while (count.get() == capacity) {
                notFull.await();
            }
            //新节点放入链表的尾端
            enqueue(node);
            c = count.getAndIncrement();
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            putLock.unlock();
        }
        if (c == 0)
            signalNotEmpty();
    }
     /**
     * Links node at end of queue.
     *
     * @param node the node
     */
    private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }

(3)看队列的poll方法(take自己查)

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
        E x = null;
        int c = -1;
        long nanos = unit.toNanos(timeout);
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
        	//如果队列为空并且超时了,直接返回null,而take不同
            while (count.get() == 0) {
                if (nanos <= 0)
                    return null;
                nanos = notEmpty.awaitNanos(nanos);
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }
     /**
     * Removes a node from head of queue.
     *
     * @return the node
     */
    private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        // assert head.item == null;
        Node<E> h = head;
        Node<E> first = h.next;
        h.next = h; // help GC
        head = first;
        E x = first.item;
        first.item = null;
        return x;
    }

3、SynchronousQueue(特殊的链表队列)

一种无缓冲(没有容量,始终为0)的等待队列。实际是使用链表的方式实现,可以认为是特殊的链表,不光node节点的数据结构特殊,取数和存数的逻辑也做了特殊化处理

先看构造方法

/**
     * Creates a {@code SynchronousQueue} with the specified fairness policy.
     * 
     * @param fair if true, waiting threads contend in FIFO order for
     *        access; otherwise the order is unspecified.
     * 如果fair是true,则按先进先出,TransferQueue,否则就是先进后出
     */
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }
     /**
     * Adds the specified element to this queue, waiting if necessary for
     * another thread to receive it.
     *
     * @throws InterruptedException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
     */
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        if (transferer.transfer(e, false, 0) == null) {
            Thread.interrupted();
            throw new InterruptedException();
        }
    }
    /**
     * Retrieves and removes the head of this queue, if another thread
     * is currently making an element available.
     *
     * @return the head of this queue, or {@code null} if no
     *         element is available
     */
    public E poll() {
        return transferer.transfer(null, true, 0);
    }

不管是put还是poll都是调用的transferer.transfer,而transferer是一个抽象类,

/**
     * Shared internal API for dual stacks and queues.
     */
    abstract static class Transferer<E> {
        /**
         * Performs a put or take.
         *
         * @param e if non-null, the item to be handed to a consumer;
         *          if null, requests that transfer return an item
         *          offered by producer.
         * 如果非空,则将项目交给消费者; 如果为 null,则传输请求返回生产者提供的一个item
         * @param timed if this operation should timeout
         * @param nanos the timeout, in nanoseconds
         * @return if non-null, the item provided or received; if null,
         *         the operation failed due to timeout or interrupt --
         *         the caller can distinguish which of these occurred
         *         by checking Thread.interrupted.
         */
        abstract E transfer(E e, boolean timed, long nanos);
    }

SynchronousQueue构造方法在初始化时选择不同的transferer实现,则存储和取数逻辑也会不一样。

(1)、TransferQueue(先进先出)

1)node的数据结构

  /** 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
            final boolean isData;
		.....
	}

2) transfer的实现

transfer 方法的实现,取头存尾

 		volatile QNode next;          // next node in queue
        volatile Object item;         // CAS'ed to or from null
        volatile Thread waiter;       // to control park/unpark
        final boolean isData;
  /** Head of queue */
        transient volatile QNode head;
        /** Tail of queue */
        transient volatile QNode tail;
 /**
         * Puts or takes an item.
         */
        @SuppressWarnings("unchecked")
        E transfer(E e, boolean timed, long nanos) {
            /* Basic algorithm is to loop trying to take either of
             * two actions:
             *
             * 1. If queue apparently empty or holding same-mode nodes,
             *    try to add node to queue of waiters, wait to be
             *    fulfilled (or cancelled) and return matching item.
             *
             * 2. If queue apparently contains waiting items, and this
             *    call is of complementary mode, try to fulfill by CAS'ing
             *    item field of waiting node and dequeuing it, and then
             *    returning matching item.
             *
             * In each case, along the way, check for and try to help
             * advance head and tail on behalf of other stalled/slow
             * threads.
             *
             * The loop starts off with a null check guarding against
             * seeing uninitialized head or tail values. This never
             * happens in current SynchronousQueue, but could if
             * callers held non-volatile/final ref to the
             * transferer. The check is here anyway because it places
             * null checks at top of loop, which is usually faster
             * than having them implicitly interspersed.
             */

            QNode s = null; // constructed/reused as needed
            //判断入参 e是否有数据,判断是存数据还是取数据
            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;
                    if (t != tail)                  // inconsistent read
                        continue;
                    if (tn != null) {               // lagging tail
                        advanceTail(t, tn);
                        continue;
                    }
                    if (timed && nanos <= 0)        // can't wait
                        return null;
                    if (s == null)
                        s = new QNode(e, isData);
                    if (!t.casNext(null, s))        // failed to link in
                        continue;
					//把新数据加到链表的尾部
                    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
                        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 {                            // complementary-mode
                	//取数逻辑
                    QNode m = h.next;               // node to fulfill
                    if (t != tail || m == null || h != head)
                        continue;                   // inconsistent read

                    Object x = m.item;
                    if (isData == (x != null) ||    // m already fulfilled
                        x == m ||                   // m cancelled
                        !m.casItem(x, e)) {         // lost CAS
                        advanceHead(h, m);          // dequeue and retry
                        continue;
                    }
					//链表的head换了,因为已经出去了
                    advanceHead(h, m);              // successfully fulfilled
                    LockSupport.unpark(m.waiter);
                    return (x != null) ? (E)x : e;
                }
            }
        }

(2)、TransferStack (后进先出)

1)node数据结构

  /** Node class for TransferStacks. */
        static final class SNode {
            volatile SNode next;        // next node in stack
            volatile SNode match;       // the node matched to this
            volatile Thread waiter;     // to control park/unpark
            Object item;                // data; or null for REQUESTs
            int mode;
            ....
         }   

2) transfer的实现逻辑

transfer的实现是下面这个类,实际上是用链表实现了栈的功能,取头存头,

    /** Node class for TransferStacks. */
        static final class SNode {
            volatile SNode next;        // next node in stack
            volatile SNode match;       // the node matched to this
            volatile Thread waiter;     // to control park/unpark
            Object item;                // data; or null for REQUESTs
            int mode;
     /** The head (top) of the stack */
        volatile SNode head;
   /**
         * Puts or takes an item.
         */
        @SuppressWarnings("unchecked")
        E transfer(E e, boolean timed, long nanos) {
            /*
             * Basic algorithm is to loop trying one of three actions:
             *
             * 1. If apparently empty or already containing nodes of same
             *    mode, try to push node on stack and wait for a match,
             *    returning it, or null if cancelled.
             *
             * 2. If apparently containing node of complementary mode,
             *    try to push a fulfilling node on to stack, match
             *    with corresponding waiting node, pop both from
             *    stack, and return matched item. The matching or
             *    unlinking might not actually be necessary because of
             *    other threads performing action 3:
             *
             * 3. If top of stack already holds another fulfilling node,
             *    help it out by doing its match and/or pop
             *    operations, and then continue. The code for helping
             *    is essentially the same as for fulfilling, except
             *    that it doesn't return the item.
             */

            SNode s = null; // constructed/reused as needed
            int mode = (e == null) ? REQUEST : DATA;

            for (;;) {
                SNode h = head;
                if (h == null || h.mode == mode) {  // empty or same-mode
                //当head为空或者head的mode和下次请求的mode一致时,走这个逻辑,代表相邻的两次请求mode一致就走这个
                    if (timed && nanos <= 0) {      // can't wait
                        if (h != null && h.isCancelled())
                            casHead(h, h.next);     // pop cancelled node
                        else
                            return null;
                    } else if (casHead(h, s = snode(s, e, h, mode))) { 
                        SNode m = awaitFulfill(s, timed, nanos);
                        if (m == s) {               // wait was cancelled
                            clean(s);
                            return null;
                        }
                        if ((h = head) != null && h.next == s)
                            casHead(h, s.next);     // help s's fulfiller
                        return (E) ((mode == REQUEST) ? m.item : s.item);
                    }
                } else if (!isFulfilling(h.mode)) { // try to fulfill
                	//链表中的头head节点的mode是REQUEST或者是FULFILLING,
                	//而此时队列又新加一个数据(mode为DATA),就有下面的处理流程
                    if (h.isCancelled())            // already cancelled
                        casHead(h, h.next);         // pop and retry
                    else if (casHead(h, s=snode(s, e, h, FULFILLING|mode))) {
                        for (;;) { // loop until matched or waiters disappear
                            SNode m = s.next;       // m is s's match
                            if (m == null) {        // all waiters are gone
                                casHead(s, null);   // pop fulfill node
                                s = null;           // use new node next time
                                break;              // restart main loop
                            }
                            SNode mn = m.next;
                            if (m.tryMatch(s)) {
                                casHead(s, mn);     // pop both s and m
                                return (E) ((mode == REQUEST) ? m.item : s.item);
                            } else                  // lost match
                                s.casNext(m, mn);   // help unlink
                        }
                    }
                } else {                            // help a fulfiller
                	//如果head节点是DATA,而下次请求的mode是非DATA的,则把head弹出,
                	//因为此head数据已经被消费了,相当于消费过还遗留在队列中的过期数据
                    SNode m = h.next;               // m is h's match
                    if (m == null)                  // waiter is gone
                        casHead(h, null);           // pop fulfilling node
                    else {
                        SNode mn = m.next;
                        if (m.tryMatch(h))          // help match
                            casHead(h, mn);         // pop both h and m
                        else                        // lost match
                            h.casNext(m, mn);       // help unlink
                    }
                }
            }
        }
       } 

你可能感兴趣的:(#,并发编程,java,java)