PriorityBlockingQueue

最近一直在忙公司新业务流程引擎,耽误了最后一个阻塞队列,之后把流程引擎结合springboot总结一下

PriorityBlockingQueue

描述:

带排序的BlockingQueue实现,采用ReentrantLock控制并发,队列为无界队列,与其他阻塞队列不同的是PriorityBlockingQueue只能指定初始队列大小,后面插入元素的时候,如果空间不够的话会自动扩容。ArrayBlockingQueue是有界队列,LinkedBlockingQueue可以是无界队列(就是容量为Integer.MAX_VALUE),其实就是容量非常大的有界队列。是PriorityBlockingQueue的贤臣安全版本。不可以插入null值(阻塞队列应该都不允许插入null值,印象中,还有去校验)。插入队列的对象必须是可以比较大小的(comparable),否则报ClassCastException异常。

存储结构介绍

PriorityBlockingQueue是基于数组实现的二叉堆,对于元素a[i],其左子节点a[2*i+1],其右子节点a[2*i+2],其父节点a[(i-1)/2],而且每个节点的值都小于左右节点,也就是说根节点是二叉堆中最小的值。

示意图

属性介绍

//默认的容量

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;

 

//

private final Condition notEmpty;

 

//再进行扩容时 需要获取这个锁

private transient volatile int allocationSpinLock;

 

//用于序列化和反序列化时使用

private PriorityQueue<E> q;

 

构造器

//默认容量 没有指定比较器

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];

}

 

//可以传入一个集合

public PriorityBlockingQueue(Collection<? extends E> c) {

    this.lock = new ReentrantLock();

    this.notEmpty = lock.newCondition();

    boolean heapify = true; // true if not known to be in heap order

    boolean screen = true;  // true if must screen for nulls

    if (c instanceof SortedSet<?>) {

        SortedSet<? extends E> ss = (SortedSet<? extends E>) c;

        this.comparator = (Comparator<? super E>) ss.comparator();

        heapify = false;

    }

    else if (c instanceof PriorityBlockingQueue<?>) {

        PriorityBlockingQueue<? extends E> pq =

            (PriorityBlockingQueue<? extends E>) c;

        this.comparator = (Comparator<? super E>) pq.comparator();

        screen = false;

        if (pq.getClass() == PriorityBlockingQueue.class) // exact match

            heapify = false;

    }

    Object[] a = c.toArray();

    int n = a.length;

    // If c.toArray incorrectly doesn't return Object[], copy it.

    if (a.getClass() != Object[].class)

        a = Arrays.copyOf(a, n, Object[].class);

    if (screen && (n == 1 || this.comparator != null)) {

        for (int i = 0; i < n; ++i)

            if (a[i] == null)

                throw new NullPointerException();

    }

    this.queue = a;

    this.size = n;

    if (heapify)

        heapify();

}

 

扩容方法 tryGrow

位置:PriorityBlockingQueue 289
方法目的:扩容

方法流程:

private void tryGrow(Object[] array, int oldCap) {

//先释放锁

    lock.unlock(); // must release and then re-acquire main lock

Object[] newArray = null;

//cas 将 allocationSpinLock设置为1 成功代表获取锁

    if (allocationSpinLock == 0 &&

        UNSAFE.compareAndSwapInt(this, allocationSpinLockOffset,

                                 0, 1)) {

        try {

            //如果 现在的节点数 小于 64增加 oldCap + 2 如果大于等于增加一半

            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;

            }

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

    }

}

Put方法

位置:PriorityBlockingQueue 511
public void put(E e) {
    offer(e); // never need to block
}

 

位置:PriorityBlockingQueue 511
方法目的:元素进队

方法流程:

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    int n, cap;
Object[] array;
//判断是否需要扩容  数组中的元素个数 >= 当前容量
    while ((n = size) >= (cap = (array = queue).length))
        tryGrow(array, cap);
    try {
        Comparator<? super E> cmp = comparator;
         //节点进入二叉堆
        if (cmp == null)
            siftUpComparable(n, e, array);
        else
            siftUpUsingComparator(n, e, array, cmp);
         //更新当前队列元素数
        size = n + 1;
         //唤醒读线程
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
    return true;
}

进入二叉堆方法

//插入的节点比父节点小 那么交换 然后重新比较父节点 
//那么怎么选择第一个父节点呢 二叉堆 除了最下面一层 其他层都是满二叉树 比如上面的二//叉堆 如果要进入新元素那么13是第一个父节点
private static <T> void siftUpComparable(int k, T x, Object[] array) {
    Comparable<? super T> key = (Comparable<? super T>) x;
while (k > 0) {
     //父节点
        int parent = (k - 1) >>> 1;
        Object e = array[parent];
        if (key.compareTo((T) e) >= 0)
            break;
        array[k] = e;
        k = parent;
    }
    array[k] = key;
}
 
private static <T> void siftUpUsingComparator(int k, T x, Object[] array,
                                   Comparator<? super T> cmp) {
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = array[parent];
        if (cmp.compare(x, (T) e) >= 0)
            break;
        array[k] = e;
        k = parent;
    }
    array[k] = x;
}

Take方法

位置:PriorityBlockingQueue 544
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;
}

 

位置:PriorityBlockingQueue 323
方法目的:元素出队

方法流程:

private E dequeue() {
    //队列元素大小
    int n = size - 1;
    if (n < 0)
        return null;
    else {
        Object[] array = queue;
         //对头 返回结果 也就是说先出对头 毕竟是 FIFO队列嘛
        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;
    }
}

出二叉堆方法

怎么出二叉堆呢,移除一个节点之后,将节点的左右子节点中较小的位置,上滑替换原来父节点的位置,然后依次执行.又由于结构除了最后一层满足满二叉树结构,所以最下面一层的最右边节点会补充位置

 

 
private static <T> void siftDownComparable(int k, T x, Object[] array,
                                           int n) {
    if (n > 0) {
        Comparable<? super T> key = (Comparable<? super T>)x;
         //a[n]是最后一个元素,父节点是a[(n-1)/2] 我们结合上面的图分析 17出队
        int half = n >>> 1;           // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = array[child];
            int right = child + 1;
            if (right < n &&
                ((Comparable<? super T>) c).compareTo((T) array[right]) > 0)
                c = array[child = right];
            if (key.compareTo((T) c) <= 0)
                break;
            array[k] = c;
            k = child;
        }
        array[k] = key;
    }
}
 
private static <T> void siftDownUsingComparator(int k, T x, Object[] array,
                                                int n,
                                                Comparator<? super T> cmp) {
    if (n > 0) {
        int half = n >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = array[child];
            int right = child + 1;
            if (right < n && cmp.compare((T) c, (T) array[right]) > 0)
                c = array[child = right];
            if (cmp.compare(x, (T) c) <= 0)
                break;
            array[k] = c;
            k = child;
        }
        array[k] = x;
    }
}

大家推荐个靠谱的公众号程序员探索之路,大家一起加油

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值