JAVA并发编程: PriorityQueue -》阻塞队列 PriorityBlockingQueue

生活

一旦一种新技术开始滚动碾压道路,如果你不能成为压路机的一部分,那么你就只能成为道路的一部分。

PriorityQueue

阻塞队列里的PriorityBlockingQueue基于PriorityQueue,所以在研究PriorityBlockingQueue之前要先研究一下PriorityQueue,这是一个有优先级概念的队列,是有顺序的,他的顺序是通过内部的比较器实现。
他的内部维护了一个数组,但不是一个简单的数组,其实是通过一个数组,维护一个二叉堆的数据结构。
因此先把二叉堆搞清楚。

啥是二叉堆

研究二叉堆,有必要先把堆的概念搞清楚,注意:这里指代的堆,并不是java开发中常说的堆栈的那个堆哦。
那么,堆是一种怎么样的数据结构呢?
堆通常是一个可以看成一棵树的数组对象,有以下两个特征:
1、堆的某个节点,总是不大于或者不小于父节点
2、堆是一颗完全树。

二叉堆是一种特殊的堆,二叉树是一颗完全二叉树或者近似于完全二叉树。
二叉堆又有两种,最大堆和最小堆。
最大堆:父节点总是大于等于任何一个子节点
最小堆:父节点总是小于等于任何一个子节点。

来图解表示下二叉堆

在这里插入图片描述

上图是一个二叉堆(完全二叉树),他的特点是在N层被填满之前,不会开始第N+1层的填充,并且填充的顺序是从左往右。
二叉堆又可以用数组来表示:
如下图
在这里插入图片描述
通过上图可以发现一个规律,使用数组实现的二叉堆,位置N上的元素,其左孩子在2N+1处,其右孩子在2N+2处,根节点是0。

由于ProtityQueue是一个有优先级概念的队列,因此可以使用二叉堆来实现,队列的入队和出队,也可以通过二叉堆来实现,对应到二叉堆就是他的上移和下移,下面来图解一下 二叉堆的上移和下移。

这些图都是盗来的。。自己绘画水平太差了~~

二叉堆的上移

下面图解描述一下如何向二叉堆添加一个元素:
在这里插入图片描述
图1是一个二叉堆 最小堆(完全二叉树)
图2在二叉树的最后插入一个节点2
在这里插入图片描述
图3 由于图2中2的父节点6比它大,所以2和6交换
图4 由于2的父节点5比它,所以2和5交换,
此时又是一个标准的二叉堆。

二叉堆的下移

下面来看下如何把二叉堆中第一个元素移出,即优先队列中的出队操作。

在这里插入图片描述
图1一个二叉堆
图2准备出队最小元素1,先把最后一个元素8所在的位置删除
在这里插入图片描述
图3 最小节点1下两个孩子节点,取最小节点3交换
图4 最小节点1下两个孩子节点,去最小节点4交换
在这里插入图片描述
此时根元素1的最小孩子节点9比8还大,直接用8覆盖1。1即被删除了。

PriorityQueue的创建

//容器数组
 transient Object[] queue;

//构造器很多,总的来说指定容量和比较器,如果没有指定比较器,要求存入的元素必须实现了Compareable接口,如果没有指定容量默认11
public PriorityQueue() {
//默认11
        this(DEFAULT_INITIAL_CAPACITY, null);
    }

       public PriorityQueue(int initialCapacity) {
        this(initialCapacity, null);
    }

   public PriorityQueue(Comparator<? super E> comparator) {
        this(DEFAULT_INITIAL_CAPACITY, comparator);
    }


    public PriorityQueue(int initialCapacity,
                         Comparator<? super E> comparator) {
        // Note: This restriction of at least one is not actually needed,
        // but continues for 1.5 compatibility
        if (initialCapacity < 1)
            throw new IllegalArgumentException();
        this.queue = new Object[initialCapacity];
        this.comparator = comparator;
    }
   @SuppressWarnings("unchecked")
    public PriorityQueue(Collection<? extends E> c) {
        if (c instanceof SortedSet<?>) {
            SortedSet<? extends E> ss = (SortedSet<? extends E>) c;
            this.comparator = (Comparator<? super E>) ss.comparator();
            initElementsFromCollection(ss);
        }
        else if (c instanceof PriorityQueue<?>) {
            PriorityQueue<? extends E> pq = (PriorityQueue<? extends E>) c;
            this.comparator = (Comparator<? super E>) pq.comparator();
            initFromPriorityQueue(pq);
        }
        else {
            this.comparator = null;
            initFromCollection(c);
        }
    }

    @SuppressWarnings("unchecked")
    public PriorityQueue(PriorityQueue<? extends E> c) {
        this.comparator = (Comparator<? super E>) c.comparator();
        initFromPriorityQueue(c);
    }
    @SuppressWarnings("unchecked")
    public PriorityQueue(SortedSet<? extends E> c) {
        this.comparator = (Comparator<? super E>) c.comparator();
        initElementsFromCollection(c);
    }

PriorityQueue的入队

public boolean offer(E e) {
//元素为空就报错
        if (e == null)
            throw new NullPointerException();
        modCount++;
        int i = size;
        //容量不够就扩容
        if (i >= queue.length)
        //扩容
            grow(i + 1);
        size = i + 1;
        //当是第一个元素时,就不用去比较只需要放在根节点即可
        if (i == 0)
            queue[0] = e;
        else
        //上移
            siftUp(i, e);
        return true;
    }

//扩容
private void grow(int minCapacity) {
        int oldCapacity = queue.length;
        // Double size if small; else grow by 50%
        //如果原长度小于64,则设置新容量 原来+原来的+2
        //否则 原来+原来/2
        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
        // overflow-conscious code
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        queue = Arrays.copyOf(queue, newCapacity);
    }
    //上移代码
    //根据是否有比较器选择不一样的方法
    private void siftUp(int k, E x) {
        if (comparator != null)
            siftUpUsingComparator(k, x);
        else
            siftUpComparable(k, x);
    }

    @SuppressWarnings("unchecked")
    private void siftUpComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>) x;
        while (k > 0) {
        // 比较自己与父节点,比较不满足条件就互换
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (key.compareTo((E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = key;
    }

    @SuppressWarnings("unchecked")
    private void siftUpUsingComparator(int k, E x) {
    //同上
        while (k > 0) {
            int parent = (k - 1) >>> 1;
            Object e = queue[parent];
            if (comparator.compare(x, (E) e) >= 0)
                break;
            queue[k] = e;
            k = parent;
        }
        queue[k] = x;
    }

PriorityQueue的出队

 public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;

	//取出第一个元素return出去
        E result = (E) queue[0];
        //取出最后一个元素
        E x = (E) queue[s];
        //把最后一个元素的位置置空
        queue[s] = null;
        if (s != 0)
            siftDown(0, x);
        return result;
    }
private void siftDown(int k, E x) {
        if (comparator != null)
            siftDownUsingComparator(k, x);
        else
            siftDownComparable(k, x);
    }

    @SuppressWarnings("unchecked")
    private void siftDownComparable(int k, E x) {
        Comparable<? super E> key = (Comparable<? super E>)x;
     //之所以循环一半,是因为超过一半的 2n+1就超过这个数组长度,也就是没有子节点了。 
        int half = size >>> 1;        // loop while a non-leaf
        while (k < half) {
            int child = (k << 1) + 1; // assume left child is least
            Object c = queue[child];
            int right = child + 1;

		//比较两个孩子节点,取最小或者最大的交换
            if (right < size &&
                ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
                c = queue[child = right];
            if (key.compareTo((E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        //把最后一个元素放置在对应的位置上
        queue[k] = key;
    }

    @SuppressWarnings("unchecked")
    private void siftDownUsingComparator(int k, E x) {
    //同上
        int half = size >>> 1;
        while (k < half) {
            int child = (k << 1) + 1;
            Object c = queue[child];
            int right = child + 1;
            if (right < size &&
                comparator.compare((E) c, (E) queue[right]) > 0)
                c = queue[child = right];
            if (comparator.compare(x, (E) c) <= 0)
                break;
            queue[k] = c;
            k = child;
        }
        queue[k] = x;
    }

PriorityQueue的heapify

回到上面创建优先队列的构造器中,有一些是直接通过一个集合来创建,这里的方法是先把集合里的元素转成一个数组,然后通过heapify方法把这个数组变成一个标准的二叉堆,本质的实现原理就是下移:

 private void heapify() {
        for (int i = (size >>> 1) - 1; i >= 0; i--)
            siftDown(i, (E) queue[i]);
    }

PriorityBlockingQueue

PriorityBlockingQueue无非就死在PriorityQueue的基础上加上锁和条件变量

 /**
     * Lock used for all public operations
     */
    private final ReentrantLock lock;

    /**
     * Condition for blocking when empty
     */
    private final Condition notEmpty;

注意到条件变量仅有notEmpty,用以阻塞当队列为空时的出队线程。
为啥没有notFull,因为本身有扩容的操作。所以不存在容量上限的情况。

案例代码


public class PBQTest {

    public static void main(String[] args) {
        PriorityBlockingQueue queue = new PriorityBlockingQueue();
        new Thread(new PBQ(queue,new Student("小明",20))).start();
        new Thread(new PBQ(queue,new Student("小红",17))).start();
        new Thread(new PBQ(queue,new Student("小刚",25))).start();
        new Thread(new PBQ(queue,new Student("小慌",31))).start();
        new Thread(new PBQ(queue,null)).start();
        new Thread(new PBQ(queue,null)).start();
        new Thread(new PBQ(queue,null)).start();
        new Thread(new PBQ(queue,null)).start();

    }

    static class PBQ implements  Runnable{

        private PriorityBlockingQueue queue;
        private Student student;

        @Override
        public void run() {

            if(student == null){
                try {
                    Thread.sleep(Long.valueOf(new Random().nextInt(5000)));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    System.out.println(queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            else{
                queue.offer(student);
            }
        }

        public PBQ(PriorityBlockingQueue queue, Student student) {
            this.queue = queue;
            this.student = student;
        }
    }

    static class Student implements  Comparable<Student>{

        @Override
        public int compareTo(Student o) {
            return this.age>o.age?1:0;
        }

        private String name;
        private int age;

        public Student(String name, int age) {
            this.name = name;
            this.age = age;
        }

        @Override
        public String toString() {
            return String.format("name:%s,age:%s",name,age);
        }
    }
}
name:小红,age:17
name:小明,age:20
name:小刚,age:25
name:小慌,age:31

猜你喜欢

转载自blog.csdn.net/qq_28605513/article/details/84679521