【集合】PriorityBlockingQueue 源码分析

前言

Github:https://github.com/yihonglei/jdk-source-code-reading(java-concurrent)

一 概述

PriorityBlockingQueue 基于数组实现的顺序队列,有界队列,默认 数组大小 11。

二 PriorityBlockingQueue 实例

package com.jpeony.concurrent.queue;

import java.util.concurrent.PriorityBlockingQueue;

/**
 * @author yihonglei
 */
public class PriorityBlockingQueueTest {
    public static void main(String[] args) throws InterruptedException {
        PriorityBlockingQueue<String> priorityQueue = new PriorityBlockingQueue<>();

        priorityQueue.offer("one");

        System.out.println(priorityQueue.take());
    }
}

三 PriorityBlockingQueue 底层实现

主要分析 put()/offer() 入队操作,take()/poll() 出队操作。

重要属性

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;

    /** 基于 Object 类型数组实现队列 */
    private transient Object[] queue;

    /** 队列实际元素大小 */
    private transient int size;

    /** 队列元素排序实现 */
    private transient Comparator<? super E> comparator;

    /** 加锁机制 */
    private final ReentrantLock lock;

    /** take() 队列为空时,阻塞线程条件 */
    private final Condition notEmpty;

    // ......
}

构造器

创建 PriorityBlockingQueue 队列,默认数组大小 11,可以指定大小,可以指定队列元素

比较方法。

/**
 * 默认构造器
 */
public PriorityBlockingQueue() {
    // 默认大小 11
    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();
    // take() 队列为空,阻塞线程,等待被唤醒条件
    this.notEmpty = lock.newCondition();
    // 比较机制
    this.comparator = comparator;
    // 初始化数组大小
    this.queue = new Object[initialCapacity];
}

PriorityBlockingQueue#put()/offer()

offer()

非阻塞方式入队。 

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    final ReentrantLock lock = this.lock;
    lock.lock();
    // 声明元素个数 n,数组 array
    int n, cap;
    Object[] array;
    // 赋值 n = size,array = queue,cap = array.length,如果满了,进行扩容操作
    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);
        // 实际元素个数加 1
        size = n + 1;
        // 唤醒 take() 时因为队列为空被阻塞的线程,重新尝试获取元素
        notEmpty.signal();
    } finally {
        lock.unlock();
    }
    return true;
}

tryGrow() 扩容操作

/**
 * 扩容操作,通过 CAS 加轻量级锁
 */
private void tryGrow(Object[] array, int oldCap) {
    lock.unlock(); // must release and then re-acquire main lock
    Object[] newArray = null;
    // CAS 轻量级锁加锁,避免并发扩容
    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;
            }
            // 创建新数组
            if (newCap > oldCap && queue == array)
                newArray = new Object[newCap];
        } finally {
            allocationSpinLock = 0;
        }
    }
    // 并发扩容时,线程让出 cpu 执行时间,给其他线程执行,自己稍后执行
    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);
    }
}

siftUpComparable() 默认排序 和 siftUpUsingComparator() 自定义排序操作。 

/**
 * 默认排序
 */
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;
}

put()

 PriorityBlockingQueue 的 put() 不需要阻塞,跟 LinkedBlockingQueue 和 ArrayBlockingQueue

不一样。

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

PriorityBlockingQueue#take()/poll()

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

dequeue() 出队操作。 

/**
 * 出队
 */
private E dequeue() {
    // 判断队列是否为空
    int n = size - 1;
    if (n < 0)
        return null;
    else {
        // 获取数组下标为0的元素,队列出队
        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;
    }
}

siftDownComparable() 或 siftDownUsingComparator,判断数组是否需要移出重排序右移。

/**
 * 判断数组是否需要重排序右移,默认比较方法
 */
private static <T> void siftDownComparable(int k, T x, Object[] array,
                                           int n) {
    if (n > 0) {
        Comparable<? super T> key = (Comparable<? super T>)x;
        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;
    }
}

  poll()

 非阻塞式出队。

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

四 总结

1、PriorityBlockingQueue 顺序阻塞有界队列,默认数组大小 11;

2、入队元素支持 默认或自定义排序;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值