PriorityBlockingQueue

特点

  • 这是一个优先级队列,该队列中的元素必须是Compartor或者Comparable的实现类才可以;
  • 这是一个无界队列,所以入队不需要等待,直接将元素写入即可,出队时如果队列中没有元素需要在Condition上等待,这样也说明了该队列只需要一个Condition即可;
  • 该类的底层使用的是数组,成员变量有最大容量的最大值为Integer.MAX_VALUE,但在该类初始化数组的时候并没有判断最大值,所以数组的大小还是可以大于该值,但如果在写入元素时因为内存不够等问题,会抛出OOM异常。
  • 底层数据结构使用的是小根堆和大根堆,即最小值或者最大值(优先级最高)的元素排在根部,每次取出元素时只需要获取根部元素即可,然后再对剩余的元素重新排序

基本结构

off(E e)

入队操作,需要加锁,不支持中断操作,因为是无界队列,所以不需要Condition等待条件

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;
}
  • 如果e为null,则抛出NPE
  • 获取ReentrantLock并加锁,这是一个非中断锁
  • 将size赋值给n,queue赋值给array获取array的长度赋值给cap,如果n大于等于了cap说明数组的元素个数大于等于了数组的长度需要进行扩容操作,调用tryGrow方法进行扩容
  • 获取comparator比较器,如果为null使用默认比较器进行比较,调用siftUpComparable方法进行小顶堆的排序;如果不为null就使用指定的排序器进行排序并构造小顶堆
  • 元素个数size+1操作
  • 唤醒出队操作中等待的线程并返回true
  • 最后进入finally代码块进行锁的释放

add(E e)

add操作其实调用的就是offer方法

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

put(E e)

put操作和add操作一样,都是直接调用offer方法

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

take(E e)

从优先队列中获取元素,如果元素为null,则在Condition队列上进行等待,直到队列中有元素

    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;
    }
  • 获取ReentrantLock锁并加锁,该锁是支持中断操作
  • 进入while循环,调用dequeue方法从队列中获取元素,如果为null,调用await方法进行等待;如果不为null,将该元素从队列中删除并重新构造小顶堆,返回该元素
  • 进入finally代码块释放锁

poll

该方法也是从队列中获取元素,它和take的区别在于:

  1. 该方法加的锁是不支持中断操作的
  2. 调用dequeue方法获取元素,如果为null,直接返回,不进行等待
    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return dequeue();
        } finally {
            lock.unlock();
        }
    }

peek

该方法也是获取元素,但只是获取,并不会将该元素从队列中移除

    public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (size == 0) ? null : (E) queue[0];
        } finally {
            lock.unlock();
        }
    }

size

获取队列中元素的个数,这个变量和数组的长度不一定相等,该方法需要加锁,因为要防止在调用该方法时别的线程往该队列中写入元素

    public int size() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return size;
        } finally {
            lock.unlock();
        }
    }
posted @ 2021-07-18 10:51  扭不动的奥利奥  阅读(98)  评论(0)    收藏  举报