PriorityBlockingQueue源码详解

PriorityBlockingQueue简介

PriorityBlockingQueue是PriorityQueue的并发容器,是线程安全的。

PriorityQueue是一个完全二叉树,对每个节点都进行编号。
在这里插入图片描述
如果从1开始编号的话,parent和节点有一定的关系。
parent.id = child.id / 2;
leftChild.id = parent.id * 2 ;
rightChild.id = parent.id * 2 + 1;

由于这个特性,所以PriorityQueue可以用数组来存储,十分方便!

PriorityBlockingQueue源码分析

主要属性

//存放数据的数组
private transient Object[] queue;
//元素个数
private transient int size;

//排序规则
private transient Comparator<? super E> comparator;

//独占锁
private final ReentrantLock lock;

//队列为空的时候的阻塞队列
private final Condition notEmpty;

offer()

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)
            //如果cmp==null,按照key的compare()方法来排列
            siftUpComparable(n, e, array);
        else//按照cmp排列
            siftUpUsingComparator(n, e, array, cmp);
        size = n + 1;//size++
        //唤醒出队等待线程
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
    return true;
}
//siftUpUsingComparator()是往上堆化的过程。向上堆化过程也十分简单,就是如果child节点与parent节点不符合排序规则,就将child和parent换位置。
private static <T> void siftUpUsingComparator(int k, T x, Object[] array, Comparator<? super T> cmp) {
    while (k > 0) {//k就是当前节点的下标,最小为0
        //获取parent下标,parent是从0开始编号,感兴趣的同学可以自己去模拟一下parent和child之间的关系。
        
        int parent = (k - 1) >>> 1;
        Object e = array[parent];
        //如果compare()>=0,说明就满足排序规则了
        if (cmp.compare(x, (T) e) >= 0)
            break;
        //将parent的位置与child调换位置
        array[k] = e;
        k = parent;
    }
    array[k] = x;
}

add()

 public boolean add(E e) {
     return offer(e);
 }

add()和offer()效果一样

put()

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

put()、add()、offer()效果一样

take()

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    //可中断式申请锁
    lock.lockInterruptibly();
    E result;
    try {
        //如果队列为空,就阻塞在notEmpty队列上
        while ( (result = dequeue()) == null)
            notEmpty.await();
    } finally {
        lock.unlock();
    }
    return result;
}

private E dequeue() {
    int n = size - 1;
    //如果队列为空,返回null
    if (n < 0)
        return null;
    else {
        //第0个元素就是栈顶元素
        Object[] array = queue;
        E result = (E) array[0];
        //将第n个元素设置为栈顶元素,然后利用array[n]作为标准来向下堆化
        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;
    }
}

siftDownUsingComparator()
向下堆化,就是从左儿子和右儿子之中跳出符合排序规则的点与parent调换,然后再将被调换的儿子节点作为parent节点继续执行这个过程,直至符合排序规则

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;
            //如果右儿子比左儿子更满足排序规则,就将c设置为右儿子
            if (right < n && cmp.compare((T) c, (T) array[right]) > 0)
                c = array[child = right];
            //如果两个儿子和parent相比都不满足规则,就退出循环,向下堆化结束
            if (cmp.compare(x, (T) c) <= 0)
                break;
            //如果儿子节点满足排序规则,就将儿子节点挪到父亲节点上   
            array[k] = c;
            k = child;
        }
        array[k] = x;
    }
}

poll()

public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return dequeue();
    } finally {
        lock.unlock();
    }
}

与take()相比,队列为空时,poll()不会阻塞,会直接返回null。

总结

PriorityBlockingQueue就是引入了一个ReentrantLock来保证了线程并发安全,其他和PriorityQueue基本一致!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值