PriorityBlockingQueue

1.PriorityQueue

1.1二叉堆

用数组实现的二叉树
堆总是一棵完全二叉树。

最大堆:父结点的键值总是大于或等于任何一个子节点的键值;最小堆:父结点的键值总是小于或等于任何一个子节点的键值。

1.2PriorityQueue

一个基于优先级的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序。插入队列的对象必须是可比较大小的.

优先队列只保证poll出来的值是最大值(或者最小值),虽然实现了 Collection 和 Iterator 接口中的所有接口方法,但是对其对象进行迭代并遍历时,不能保证有序性。如果想要实现有序遍历,建议采用 Arrays.sort(queue.toArray()) 进行处理.

没有指定Comparator:

  PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.add(13);
        queue.add(-3);
        queue.add(20);
        queue.add(-25);
        System.out.println(queue);
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());


输出:
-25
-3
13
20

指定Comparator:

public class Student {
    private String name;

    private Integer score;
}

public static void main(String[] args) {
        Comparator<Student> scoreComparator = new Comparator<Student>() {
            public int compare(Student c1, Student c2) {
                if (c1.getScore() > c2.getScore()) {
                    return -1;
                } else if (c1.getScore() < c2.getScore()) {
                    return 1;
                } else return 0;
            }
        };
        PriorityQueue<Student> priorityQueue = new PriorityQueue<>(11, scoreComparator);
        priorityQueue.add(new Student("A", 99));
        priorityQueue.add(new Student("B", 100));
        priorityQueue.add(new Student("C", 89));
        priorityQueue.add(new Student("D", 98));

        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());
        System.out.println(priorityQueue.poll());

    }

输出:
Student{name='B', score=100}
Student{name='A', score=99}
Student{name='D', score=98}
Student{name='C', score=89}

代码详解:

//构造方法中,如果不指定大小的话,默认大小为 11
private static final int DEFAULT_INITIAL_CAPACITY = 11;
//能扩容的最大值
 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

//存放数据的数组 
//the two children of queue[n] are queue[2*n+1] and queue[2*(n+1)].
transient Object[] queue; 

//当前大小  
private int size = 0;

// 大小比较器,如果不指定,按照自然序排序
private final Comparator<? super E> comparator;

//队列被改变的次数  
transient int modCount = 0; 



public boolean add(E e) {
     return offer(e);
}
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;
     // 如果节点个数小于 64,那么增加的 oldCap + 2 的容量
       // 如果节点数大于等于 64,那么增加 oldCap 的一半
     int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                                         (oldCapacity + 2) :
                                         (oldCapacity >> 1));
     //设置最大值  
     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;
    }

小顶堆中,向优先队列中插入元素E时,首先获取E的父节点元素parent(queue[(E的下标位置-1)/2]),然后E和parent比较,如果parent小于E,则E的位置为队列尾;如果parent大于E,则两者的位置互换,然后E再和Parent的parent比较。

public E poll() {
        if (size == 0)
            return null;
        int s = --size;
        modCount++;
        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;
        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;
    }

小顶堆中,删除第一个元素E=queue[0] ,首先获取E的l两个子节点L= queue[1] 和R=queue[2],然后L和R比较,选出最小值min(L,R)=c=queue[n],设置queue[0]=c;再对c原先的子节点进行比较,找出最小值,设置为queue[n]的值。

 

2.PriorityBlockingQueue

PriorityQueue 的线程安全版本,能自动扩容的无界队列.使用ReentrantLock+Condition控制.

   
    //构造方法中,如果不指定大小的话,默认大小为 11
    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    //存放数据的数组 
    //the two children of queue[n] are queue[2*n+1] and queue[2*(n+1)].  
    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;
    public void put(E e) {
        offer(e); // 无边队列,不会阻塞
    } 

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


    private void tryGrow(Object[] array, int oldCap) {
        lock.unlock(); //释放独占锁,扩容操作和读操作可以同时进行,提高吞吐量。
        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;
                }
//如果 queue != array,说明在allocationSpinLock CAS之前,有其他线程给 queue 分配了其他的空间
                if (newCap > oldCap && queue == array)
                    newArray = new Object[newCap];
            } finally {
                 // 重置,也就是释放锁
                allocationSpinLock = 0;
            }
        }
        if (newArray == null) // 其他的线程也在做扩容的操作
            Thread.yield();
        lock.lock();// 重新获取锁
        if (newArray != null && queue == array) {
            queue = newArray;
            System.arraycopy(array, 0, newArray, 0, oldCap);
        }
    }

 

©️2020 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值