源码分析BlockingQueue实现(jdk1.8)

BlockingQueue

BlockingQueue常常运用于线程池的阻塞队列中,顾名思义,它能够作为一个具有阻塞作用先进先出队列。
BlockingQueue 对插入操作、移除操作、获取元素操作提供了四种不同的方法用于不同的场景中使用:1、抛出异常;2、返回特殊值(null 或 true/false,取决于具体的操作);3、阻塞等待此操作,直到这个操作成功;4、阻塞等待此操作,直到成功或者超时指定时间。总结如下:

throw exception special value blocks times out
insert add(e) offer(e) put(e) offer(e,time,unit)
delete remove() poll() take() poll(time,unit)
examine element() peek() not applicable not applicable

实现1:ArrayBlockingQueue

ArrayBlockingQueue:基于数组结构的有界阻塞队列,并发控制通过ReentrantLock获得锁来实现。
并发同步原理:读操作和写操作都需要获取到 AQS 独占锁才能进行操作【对比之下LinkedBlockingQueue有两个锁,分别为读取锁和写入锁,能同时读和写】。如果队列为空,这个时候读操作的线程进入到读线程队列排队,等待写线程写入新的元素,然后唤醒读线程队列的第一个等待线程。如果队列已满,这个时候写操作的线程进入到写线程队列排队,等待读线程将队列元素移除腾出空间,然后唤醒写线程队列的第一个等待线程。

属性及构造函数

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
     /**
     * 存放元素的数组
     */
    final Object[] items;

    /**
     * 下一次读取操作的位置
     */
    int takeIndex;

    /**
     * 下一次存放操作的位置
     */
    int putIndex;

    /**
     * 队列中对象数量
     */
    int count;
   
   //下面三个为控制并发用到的同步器
    final ReentrantLock lock;
    private final Condition notEmpty;
    private final Condition notFull;

   //设定队列容量的构造函数,默认非公平锁
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }
   //设定队列容量和锁公平性的构造函数
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull = lock.newCondition();
    }

//指定集合的构造函数,将此集合中的元素在构造方法期间就先添加到队列中。
 public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);
        final ReentrantLock lock = this.lock;
        lock.lock(); // Lock only for visibility, not mutual exclusion
        try {
            int i = 0;
            try {
                for (E e : c) {
                    checkNotNull(e);
                    items[i++] = e;
                }
            } catch (ArrayIndexOutOfBoundsException ex) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;
        } finally {
            lock.unlock();
        }
    }
}

put

    public void put(E e) throws InterruptedException {
        //元素不能为null
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        //可中断锁
        lock.lockInterruptibly();
        try {
            while (count == items.length)
                notFull.await();
            enqueue(e);
        } finally {
            lock.unlock();
        }
    }

    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();
    }

take

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            while (count == 0)
                notEmpty.await();
            return dequeue();
        } finally {
            lock.unlock();
        }
    }
    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;
    }

实现2:LinkedBlockingQueue

LinkedBlockingQueue:基于链表结构的“无界”阻塞队列,并发控制同样通过ReentrantLock获得锁来实现,不过LinkedBlockingQueue采用了双锁:读取锁和写入锁,相比ArrayBlockingQueue能够同时进行读和写,吞吐量更高! 线程池Executors.newFixedThreadPool()采用了此队列。
并发同步原理

使用了两个锁,两个Condition
takeLocknotEmpty 搭配:如果获取(take)一个元素,首先要获得锁(takeLock),这还不够,还要继续判断队列是否为空(notEmpty)这个条件(Condition)
putLocknotFull 搭配:如果插入(put)一个元素,首先要获得锁(putLock),这还不够,还要继续判断队列是否已满(notFull)这个条件(Condition)

属性及构造函数

public class LinkedBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    
    //很简单的结点结构,包含了该结点的元素和后继结点    
    static class Node<E> {
        E item;
        Node<E> next;
		Node(E x) { item = x; }
    }

 	//队列容量
    private final int capacity;

   //队列中元素数量
    private final AtomicInteger count = new AtomicInteger();

    //队头结点
    transient Node<E> head;

    //队头=尾结点
    private transient Node<E> last;

    //元素读取操作时需要获得的锁
    private final ReentrantLock takeLock = new ReentrantLock();

    private final Condition notEmpty = takeLock.newCondition();

    //元素存入操作时需要获得的锁
    private final ReentrantLock putLock = new ReentrantLock();

    private final Condition notFull = putLock.newCondition();

    //因为默认队列长度大小为有符号整数的最大值,所以我们称其为“无界队列”
    public LinkedBlockingQueue() {
        this(Integer.MAX_VALUE);
    }

    //可设定初始队列容量的构造函数,这样它就有界了
    public LinkedBlockingQueue(int capacity) {
        if (capacity <= 0) throw new IllegalArgumentException();
        this.capacity = capacity;
        //初始化空的头结点
        last = head = new Node<E>(null);
    }
    //利用集合初始化的构造函数
    public LinkedBlockingQueue(Collection<? extends E> c) {
        this(Integer.MAX_VALUE);
        final ReentrantLock putLock = this.putLock;
        putLock.lock(); // Never contended, but necessary for visibility
        try {
            int n = 0;
            for (E e : c) {
                if (e == null)
                    throw new NullPointerException();
                if (n == capacity)
                    throw new IllegalStateException("Queue full");
                enqueue(new Node<E>(e));
                ++n;
            }
            count.set(n);
        } finally {
            putLock.unlock();
        }
    }

put

      public void put(E e) throws InterruptedException {
        //元素不能为null
        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.
        //c初始化为-1 ,用于标识插入操作是否成功,offer方法返回布尔值就是通过c是否>=0来判断的
        int c = -1;
        Node<E> node = new Node<E>(e);
        //加锁
        final ReentrantLock putLock = this.putLock;
        final AtomicInteger count = this.count;
        putLock.lockInterruptibly();
        try {
            //队列满了则阻塞
            while (count.get() == capacity) {
                notFull.await();
            }
            //新的元素结点插到队列尾部
            enqueue(node);
            c = count.getAndIncrement();
            //c + 1 < capacity说明队列未满,调用 notFull.signal() 唤醒等待线程
            if (c + 1 < capacity)
                notFull.signal();
        } finally {
            //入队后释放锁
            putLock.unlock();
        }
        //c==0说明之前队列是空的,这时插入一个元素后队列不为空,因此唤醒notEmpty这个条件
        if (c == 0)
            signalNotEmpty();
    }

	//入队很简单,将新结点插入到队列末尾,last指针更新
    private void enqueue(Node<E> node) {
        // assert putLock.isHeldByCurrentThread();
        // assert last.next == null;
        last = last.next = node;
    }

take

    public E take() throws InterruptedException {
        E x;
        int c = -1;
        final AtomicInteger count = this.count;
        final ReentrantLock takeLock = this.takeLock;
        takeLock.lockInterruptibly();
        try {
            while (count.get() == 0) {
                notEmpty.await();
            }
            x = dequeue();
            c = count.getAndDecrement();
            if (c > 1)
                notEmpty.signal();
        } finally {
            takeLock.unlock();
        }
        if (c == capacity)
            signalNotFull();
        return x;
    }

	//出队
    private E dequeue() {
        // assert takeLock.isHeldByCurrentThread();
        // assert head.item == null;
        //头结点一直保持为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

SynchronousQueue:不存储元素的阻塞队列,即队列是虚的,不存储元素,吞吐量通常高于LinkedBlockingQueue ,线程池Executors.newCacheThreadPool()采用了此队列。

并发同步原理

当一个线程往队列中写元素时,写入操作不会立即返回,需要等待另一个线程来将这个元素拿走;同理当一个读线程做读操作的时候,同样需要一个相匹配的写线程的写操作。
这里的Synchronous指的就是写线程要和读线程同步,一个写线程匹配一个读线程。
SynchronousQueue 的队列其实是虚的,其不提供任何空间(一个都没有)来存储元素。数据必须从某个写线程交给某个读线程,而不是写到某个队列中等待被消费。

属性及构造函数

SynchronousQueue内部有一个抽象类Transferer,它的两个实现类TransferQueue和TransferStack分别用于公平和非公平模式下。
在这里插入图片描述
在这里插入图片描述

public class SynchronousQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
    
    //默认非公平模式    
	public SynchronousQueue() {
        this(false);
    }
    //设定公平模式的构造函数,公平用TransferQueue,非公平用TransferStack
    public SynchronousQueue(boolean fair) {
        transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
    }



	//抽象类Transferer中只有一个抽象方法,让我们来看一下:
    abstract static class Transferer<E> {
        /**
         * Performs a put or take.
         *
         * @param e     如果非null,表示将元素从生产者转移到消费者
         *              如果为null,表示消费者等待生产者提供元素,返回值为生产者提供的元素
         * @param timed 是否设置超时
         * @param 超时时间
         * @return 非空,则表示转移的元素;
         * 空,则表示超时或者中断。
         * 调用者可以通过线程的中断状态判断具体是哪种情况导致的
         */
        abstract E transfer(E e, boolean timed, long nanos);
    }

}

我们先采用公平模式分析源码,然后再说说公平模式和非公平模式的区别。

put和take

     public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        //根据transfer的返回值是否为null来判断是否转移元素成功
        if (transferer.transfer(e, false, 0) == null) {
            //未成功,线程中断,抛出异常
            Thread.interrupted();
            throw new InterruptedException();
        }
    }


    public E take() throws InterruptedException {
        //和put的区别在于第一个参数为null,表示取元素
        E e = transferer.transfer(null, false, 0);
        //根据transfer的返回值是否为null来判断是否转移元素成功
        if (e != null)
            return e;
        Thread.interrupted();
        throw new InterruptedException();
    }

由代码可以看出take和put操作都是通过transfer实现的,它们对此方法实现最大的区别在于方法的第一个参数,put不为null,take为null,可以说SynchronousQueue的核心功能就是通过transfer的实现的,下面来看transfer方法。


transfer

公平模式

transfer的设计思路:

  1. 调用此方法时,若队列为空或者队列中的结点和当前线程的操作类型一致(即当前操作为put操作,队列中的结点的线程属性都为写线程;take操作和读线程同理),则将当前线程加入到等待队列中
    2.如果队列中有等待结点,而且与当前操作匹配(即当前操作位put操作,队列中结点线程线程属性都为读线程,这就构成了匹配;take操作和写线程同理)。这种情况下,匹配等待队列的队头,出队,返回响应数据。

下面来看看官方注释:

           /* 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.
             */

对于上面提到的队列结点,它的结构是这样的:

       /**
         * 等待队列的结点类
         */
        static final class QNode {
            volatile QNode next;          // 只有有个后继结点指针,说明是单向链表
            volatile Object item;         // CAS'ed to or from null
            volatile Thread waiter;       // 保存线程对象,用于挂起和唤醒
            final boolean isData;          //判断是写线程结点还是读线程结点  boolean isData = (e != null);
       }

下面正式分析transfer源码:

//==================TransferQueue的transfer方法===============================
       @SuppressWarnings("unchecked")
        E transfer(E e, boolean timed, long nanos) {
            QNode s = null; // constructed/reused as needed
            boolean isData = (e != null);
            for (; ; ) {
                QNode t = tail;
                QNode h = head;
                //头结点和尾结点为空,说明还没有初始化,continue即可。
                if (t == null || h == null)         // saw uninitialized value
                    continue;                       // spin

                //队列已初始化,只有一个空结点或者当前结点与队列结点类型一致
                if (h == t || t.isData == isData) { // empty or same-mode
                    QNode tn = t.next;
                    //说明刚才有结点入队,继续continue即可
                    if (t != tail)                  // inconsistent read
                        continue;
                    if (tn != null) {               // lagging tail
                        //尾结点的后继结点不为空,说明不是真正的尾结点,CAS将后继结点设为尾结点,继续循环判断
                        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;

                    //将新结点s设为新的尾结点
                    advanceTail(t, s);              // swing tail and wait
                    Object x = awaitFulfill(s, e, timed, nanos);

                    //当这里,说明之前的线程被唤醒了

                    //x==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 分支就是上面说的第二种情况,有相应的读或写相匹配的情况
                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;
                    }

                    advanceHead(h, m);              // successfully fulfilled
                    LockSupport.unpark(m.waiter);
                    return (x != null) ? (E) x : e;
                }
            }

        Object awaitFulfill(QNode s, E e, boolean timed, long nanos) {
            /* Same idea as TransferStack.awaitFulfill */
            final long deadline = timed ? System.nanoTime() + nanos : 0L;
            Thread w = Thread.currentThread();
            //判断需要自旋的次数
            int spins = ((head.next == s) ?
                    (timed ? maxTimedSpins : maxUntimedSpins) : 0);
            //循环
            for (; ; ) {
                //如果被中断了,则取消该结点,就是将s中的item属性设为this
                if (w.isInterrupted())
                    s.tryCancel(e);
                Object x = s.item;
                //方法的唯一出口,调用tryCancel后(超时或者中断),x!=e
                if (x != e)
                    return x;
                //如果需要,检查是否超时
                if (timed) {
                    nanos = deadline - System.nanoTime();
                    if (nanos <= 0L) {
                        s.tryCancel(e);
                        continue;
                    }
                }
                if (spins > 0)
                    --spins;
                    //自旋次数已到,先检查s的线程是否为空,为空则设置为当前线程
                else if (s.waiter == null)
                    s.waiter = w;
                    //自旋次数已到,并且s也封装了当前的线程,则挂起
                else if (!timed)
                    LockSupport.park(this);
                    //自旋次数已到,当有设置超时时, spinForTimeoutThreshold 这个之前讲 AQS 的时候其实也说过,剩余时间小于这个阈值的时候,就
                    // 不要进行挂起了,自旋的性能会比较好
                else if (nanos > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanos);
            }
        }
 	}

非公平模式

通过分析TransferQueue的transfer源码大概了解了公平模式 下的put和get操作,对于非公平模式,也就是TransferStack的transfer源码实现,直接叙述不加以分析了(参考:https://www.javadoop.com/post/java-concurrent-queue)

  1. 当调用这个方法时,如果队列是空的,或者队列中的节点和当前的线程操作类型一致(如当前操作是 put 操作,而栈中的元素也都是写线程)。这种情况下,将当前线程加入到等待栈中,等待配对。然后返回相应的元素,或者如果被取消了的话,返回 null。
  2. 如果栈中有等待节点,而且与当前操作可以匹配(如栈里面都是读操作线程,当前线程是写操作线程,反之亦然)。将当前节点压入栈顶,和栈中的节点进行匹配,然后将这两个节点出栈。配对和出栈的动作其实也不是必须的,因为下面的一条会执行同样的事情。
  3. 如果栈顶是进行匹配而入栈的节点,帮助其进行匹配并出栈,然后再继续操作。
    应该说,TransferStack 的源码要比 TransferQueue 的复杂一些,如果读者感兴趣,请自行进行源码阅读。

两种模式的对比总结

公平模式

  1. 采用FIFO队列思想,队尾匹配队头出队,先进先出,体现了公平原则;
  2. 新来的线程若匹配成功,不需要入队,直接唤醒队头线程(注意:head结点是空节点,这里是指head结点的后继结点)

非公平模式:

  1. 采用栈思想,栈顶元素先匹配,先入栈的线程结点后匹配,体现了非公平原则
  2. 新来的线程若匹配成功,则需要压栈,然后新线程循环执行匹配线程逻辑,一旦发现没有并发冲突则成对出栈

这篇文章 https://zhuanlan.zhihu.com/p/29227508 用图来表示两种模式的put和take过程,清晰易懂

实现4:PriorityBlockingQueue

PriorityBlockingQueue:一个具有优先级的无界阻塞队列。优先级是因为采用堆思想,可以根据元素自身排序,也可以指定比较器进行排序;无界是因为队列能够自动扩容。

并发同步原理

当一个线程往队列中写元素或者读取元素时,会获取独占锁(ReentrantLock),和ArrayBlocking的同步实现原理很像,区别在于PriorityBlockingQueue中没有notFull这个条件(Condition)

属性及构造函数

public class PriorityBlockingQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
    //默认初始队列容量
    private static final int DEFAULT_INITIAL_CAPACITY = 11;
    //队列最大容量
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    //队列采用数组存储元素,思想是堆
    private transient Object[] queue;
    //队列中元素个数
    private transient int size;
	//比较器
	private transient Comparator<? super E> comparator;
	//独占锁
	private final ReentrantLock lock;
  	//条件Condition
    private final Condition notEmpty;
    //扩容时采用的锁,通过CAS实现
    private transient volatile int allocationSpinLock;

    //默认构造器,初始化队列大小为11
    public PriorityBlockingQueue() {
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

   //指定队列大小的构造器
    public PriorityBlockingQueue(int initialCapacity) {
        this(initialCapacity, null);
    }

    //指定队列大小和比较器的队列
    public PriorityBlockingQueue(int initialCapacity,
                                 Comparator<? super E> comparator) {
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.lock = new ReentrantLock();
        this.notEmpty = lock.newCondition();
        this.comparator = comparator;
        this.queue = new Object[initialCapacity];
    }
}

put

       public void put(E e) {
        offer(e); // never need to block
    }

      public boolean offer(E e) {
        if (e == null)
            throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        //1 获取独占锁
        lock.lock();
        int n, cap;
        Object[] array;
        //2. 插入元素前判断是否需要扩容
        while ((n = size) >= (cap = (array = queue).length))
            tryGrow(array, cap);
         //3. 插入元素并调整堆结构
        try {
            Comparator<? super E> cmp = comparator;
            //构造器为空,使用插入元素自带比较器进行调整
            if (cmp == null)
                siftUpComparable(n, e, array);
            //否则使用指定的构造器进行调整
            else
                siftUpUsingComparator(n, e, array, cmp);
            size = n + 1;
		//4. 通知notEmpty条件
            notEmpty.signal();
        } finally {
        //5. 释放锁
            lock.unlock();
        }
        return true;
    }

由于在插入元素前会判断是够需要扩容,下面来讲一下如何扩容

tryGrow扩容

   /**
     * 扩容过程中释放了独占锁,通过CAS 操作维护 allocationSpinLock状态,实现专门的扩容锁, 这样就可以保证扩容操作和读操作同时进行
     * @param array
     * @param oldCap
     */
    private void tryGrow(Object[] array, int oldCap) {
        //释放独占锁
        lock.unlock(); // must release and then re-acquire main lock
        Object[] newArray = null;
        // 用 CAS 操作将 allocationSpinLock 由 0 变为 1,算是获取扩容锁
        if (allocationSpinLock == 0 &&
            UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset,
                                     0, 1)) {
            try {
                int newCap = oldCap + ((oldCap < 64) ?
                                       (oldCap + 2) : // grow faster if small
                                       (oldCap >> 1));
                if (newCap - MAX_ARRAY_SIZE > 0) {    // possible overflow
                    int minCap = oldCap + 1;
                    if (minCap < 0 || minCap > MAX_ARRAY_SIZE)
                        throw new OutOfMemoryError();
                    newCap = MAX_ARRAY_SIZE;
                }
                //queue如果不等于array,说明有其他线程给queue分配了空间,newArray为null
                if (newCap > oldCap && queue == array)
                    newArray = new Object[newCap];
            } finally {
            //释放扩容锁
                allocationSpinLock = 0;
            }
        }
        //等待其他线程操作完毕
        if (newArray == null) // back off if another thread is allocating
            Thread.yield();
        //重新获得独占锁
        lock.lock();
        // 将原来数组中的元素复制到新分配的大数组中
        if (newArray != null && queue == array) {
            queue = newArray;
            //将旧队列中元素复制到新队列中
            System.arraycopy(array, 0, newArray, 0, oldCap);
        }
    }

take

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        //获取独占锁
        lock.lockInterruptibly();
        E result;
        try {
            while ( (result = dequeue()) == null)
                notEmpty.await();
        } finally {
        //释放独占锁
            lock.unlock();
        }
        return result;
    }

//元素出队列,并调整堆结构
    private E dequeue() {
        int n = size - 1;
        if (n < 0)
            return null;
        else {
            Object[] array = queue;
            E result = (E) array[0];
            E x = (E) array[n];
            array[n] = null;
            Comparator<? super E> cmp = comparator;
            if (cmp == null)
                siftDownComparable(0, x, array, n);
            else
                siftDownUsingComparator(0, x, array, n, cmp);
            size = n;
            return result;
        }
    }

总体来说,PriorityBlockingQueue 也是比较简单的,内部用数组实现堆结构,通过一个独占锁来控制put和take的线程安全性,通过CAS操作改变allocationSpinLockOffset状态来达到获取扩容锁的目的,这样扩容操作和读操作可以同时进行,提高吞吐量。

总结

ArrayBlockingQueue :基于数组结构的有界阻塞队列,并发控制通过一个ReentrantLock和两个Condition实现,使用生产者-消费者模式的很好选择。
LinkedBlockingQueue :基于链表结构的无界阻塞队列,可以设置初始队列容量使其有界,并发控制通过两个个ReentrantLock和两个Condition实现。
SynchronousQueue :本身不带有空间来存储任何元素,分为公平模式和非公平模式两种,公平模式通过FIFO队列思想来实现,非公平模式通过栈思想来实现。
PriorityBlockingQueue :带有优先级的无界阻塞队列,基于数组实现堆结构,能够自动扩容。

参考

https://www.javadoop.com/post/java-concurrent-queue#toc0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章