[Java concurrent programming] 18. PriorityBlockingQueue source code analysis,

Source: Internet
Author: User

[Java concurrent programming] 18. PriorityBlockingQueue source code analysis,

PriorityBlockingQueue is an array-based thread-safe unbounded queue. Its principle and internal structure are basically the same as those of PriorityQueue, but there are multiple threads for security. 1: theoretically, it is unbounded, So adding an element may cause outofmemoryerror; 2. null is not allowed. 3. the added elements are sorted by Comparator when being constructed, or the elements are naturally sorted.

PriorityBlockingQueue is based on priority, not FIFO. This is a good thing. It can be used to implement a thread pool with a higher priority and a lower priority. Like several queues we have seen before, they all inherit AbstractQueue to implement the BlockingQueue interface.

For the implementation of priority, the array is used to implement the heap. It is easy to understand the following picture:

The minimum number of heap top elements is ensured for each left and right sub-heap.

Internal Structure and structure:

// Implemented based on arrays. If the construction does not pass in the capacity, the default size is private static final int DEFAULT_INITIAL_CAPACITY = 11; /*** maximum array capacity */private static final int MAX_ARRAY_SIZE = Integer. MAX_VALUE-8;/*** priority queue array, remember that the Left and Right sub-elements of queue [n] are located in the array at queue [2 * n + 1] and queue [2 * (n + 1)] */private transient Object [] queue;/*** Number of queue elements */private transient int size;/*** comparator, which can be input during construction. If not, null is returned, use the natural sorting of elements */private transient Comparator <? Super E> comparator;/***** multiple operations on the re-entry lock control */private final ReentrantLock lock;/***** Condition queue */private final Condition notEmpty when the queue is empty; /*** spin lock */private transient volatile int allocationSpinLock;/*** use PriorityQueue for serialization. This PriorityBlockingQueue is almost identical */private PriorityQueue q; /*** default construction, use default capacity, no comparator */public PriorityBlockingQueue () {this (DEFAULT_INITIAL_CAPACITY, null);} public PriorityBlockingQueue (int InitialCapacity) {this (initialCapacity, null);}/*** final call structure */public PriorityBlockingQueue (int initialCapacity, Comparator <? Super E> comparator) {if (initialCapacity <1) throw new IllegalArgumentException (); this. lock = new ReentrantLock (); this. notEmpty = lock. newCondition (); this. comparator = comparator; this. queue = new Object [initialCapacity];}

There is nothing special about the internal structure and structure, and the priority heap is implemented based on arrays, remember the left node queue [2 * n + 1] and right node queue [2 * (n + 1)] of the array element queue [n], queue [0] is used for each departure.

Take a look at the common methods:

Add, put, and offer are the final invocation of the offer () method:

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); // if the number of elements is greater than the size of the array, the system automatically scales up. The unbounded try {Comparator <? Super E> cmp = comparator; // when you look at the input parameters during the construction, if (cmp = null) siftUpComparable (n, e, array) is naturally sorted ); // adjust else siftUpUsingComparator (n, e, array, cmp) from the bottom up for all inserts; size = n + 1; notEmpty. signal (); // notification that the queue with non-null conditions can take} finally {lock. unlock ();} return true;} // array expansion private void tryGrow (Object [] array, int oldCap) {lock. unlock (); // use the spin lock when resizing the array. The master lock is not required. Release the Object [] newArray = null; if (allocationSpinLock = 0 & UNSAFE. compareAndSwapInt (this, allocationSpinLockOffset, 0, 1) {// cas occupies the spin lock try {int newCap = oldCap + (oldCap <64 )? (OldCap + 2): // grow faster if small (oldCap> 1); // The minimum capacity here is doubled 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; // after resizing, default maximum} if (newCap> oldCap & queue = array) newArray = new Object [newCap];} finally {allocationSpinLock = 0; // release the spin lock after resizing} if (newArray = null) // Here, if this Thread is used to resize the newArray, it is definitely not null. If it is null, it means that other threads are processing the expansion, So let other threads process the Thread. yield (); lock. lock (); // re-import the lock here, because there are other operations after expansion if (newArray! = Null & queue = array) {// if this parameter is not null, copy the array queue = newArray; System. arraycopy (array, 0, newArray, 0, oldCap) ;}}// adjust 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; // obtain the parent node Object e = array [parent]; if (key. compareTo (T) e)> = 0) // if it is larger than the parent node, it does not matter to exit. It is directly placed in the k position break; array [k] = e; // It is smaller than the parent node. It is assigned to the parent node according to the k position, and continues to search for k = parent;} array [k] = key ;} // All inserts are adjusted from the bottom up. Similar to the siftUpComparable method, the comparatorprivate 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 ;}

The offer method is called at the end of all adding elements. Step 2: Scale Up + store. The general process is as follows:

1. lock to check whether the number of elements is greater than or equal to the length of the array. If yes, scale up. You do not need to use the master lock for expansion. Release the lock first and use the cas spin lock. The minimum capacity is doubled, release the spin lock and there may be competition. check whether or not to scale up. If you scale up, copy the array and add the master lock again;

2. check whether the constructor has a comparator. If the constructor has a comparator, use the comparator. If the constructor is greater than the parent node, the insert position will be directly inserted. Otherwise, it will be exchanged with the parent node and then searched up cyclically. The number is increased by 1 to notify the non-null condition queue to take and finally release the lock.

Let's take a look at several team-out operations:

Public E poll () {final ReentrantLock lock = this. lock; lock. lock (); try {return dequeue ();} finally {lock. unlock () ;}} public E take () throws InterruptedException {final ReentrantLock lock = this. lock; lock. lockInterruptibly (); // response interrupt E result; try {while (result = dequeue () = null) notEmpty. await (); // If take is specified, no elements in the array are blocked.} finally {lock. unlock ();} return result;} public E poll (long timeou T, TimeUnit unit) throws InterruptedException {long nanos = unit. toNanos (timeout); final ReentrantLock lock = this. lock; lock. lockInterruptibly (); // response interrupt E result; try {while (result = dequeue () = null & nanos> 0) nanos = notEmpty. awaitNanos (nanos); // response timeout. Check the timeout value for each wake-up.} finally {lock. unlock ();} return result;} public E peek () {final ReentrantLock lock = this. lock; lock. lock (); try {ret Urn (size = 0 )? Null: (E) queue [0]; // only gets elements, not removed} finally {lock. unlock () ;}/// this method is basically called. private E dequeue () {int n = size-1; if (n <0) return null; else {Object [] array = queue; E result = (E) array [0]; E x = (E) array [n]; // retrieve the last array element as the comparison benchmark array [n] = null; // The final array is cleared, which is equivalent to clearing the Comparator at the bottom and rightmost of the heap. <? Super E> cmp = comparator; if (cmp = null) siftDownComparable (0, x, array, n); // adjust else siftDownUsingComparator (0, x, array, n, cmp); size = n; return result ;}// adjust private static from top to bottom <T> void siftDownComparable (int k, T x, Object [] array, int n) {if (n> 0) {// The number of elements is greater than 0, and the array is not empty Comparable <? Super T> key = (Comparable <? Super T>) x; int half = n> 1; // The Position of the parent node of the last leaf node while (k 

General process:

1. Lock and obtain the queue [0]. Clear the last leaf node of the heap and use it as a comparison node. It is equivalent to moving the last leaf node to the queue [0. Then compare from top to bottom, find the position where the new queue [0] should be

2. call the top-down adjustment method: Compare the Left and Right nodes of the node to be adjusted with the previous leaf nodes. If the previous leaf nodes are the smallest, place them directly to the position to be adjusted, if the leaf node is small, take the small one and place it in the position to be adjusted. Then, repeat the small part and find it again. The number of cycles is searched by 2 points, basically, half of the number of elements is located.

Let's look at another remove. Because of the remove method, the adjustment method in 2 is used:

Public boolean remove (Object o) {final ReentrantLock lock = this. lock; lock. lock (); try {int I = indexOf (o); // locate o in the array if (I =-1) return false; removeAt (I ); // remove return true;} finally {lock. unlock () ;}// o position in the array private int indexOf (Object o) {if (o! = Null) {Object [] array = queue; int n = size; for (int I = 0; I <n; I ++) if (o. equals (array [I]) return I;} return-1;} // remove the elements at the specified position of the array // similar to the dequeue of the previous take, dequeue is the position where 0 is removed and then adjusted from the position 0. Here, private void removeAt (int I) {Object [] array = queue is adjusted from the specified position; int n = size-1; if (n = I) // removed last element array [I] = null; else {E moved = (E) array [n]; // Like dequeue, the last leaf node is used as the comparison array [n] = Null; Comparator <? Super E> cmp = comparator; if (cmp = null) siftDownComparable (I, moved, array, n); // adjust else siftDownUsingComparator (I, moved, array, n, cmp); // after being adjusted from top to bottom, if you directly place the comparison node in the position to be adjusted, it only indicates that the node is the smallest in the heap with it as the top of the heap, but it cannot be said that the maximum value is obtained from this node. // here we need to adjust the if (array [I] = moved) {if (cmp = null) again from the bottom up) siftUpComparable (I, moved, array); else siftUpUsingComparator (I, moved, array, cmp) ;}} size = n ;}

There are two adjustments during the remove operation. First, adjust them from top to bottom to minimize, and then adjust them upwards.

Source: http://blog.csdn.net/xiaoxufox/article/details/51860543

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.