Chapter 8 ArrayBlockingQueue source code parsing and arraydeque source code parsing
Note: You need to use ReentrantLock before reading this article or during the reading process. For details, see chapter 5 ReentrantLock source code parsing 1-obtain unfair lock and fair lock () chapter 6 ReentrantLock source code parsing 2 -- release Lock unlock () Chapter 7 ReentrantLock Summary
1. For ArrayBlockingQueue, master the following points:
- Create
- Join (add element)
- Team out (delete element)
2. Create
- Public ArrayBlockingQueue (int capacity, boolean fair)
- Public ArrayBlockingQueue (int capacity)
Usage:
- Queue <String> abq = new ArrayBlockingQueue <String> (2 );
- Queue <String> abq = new ArrayBlockingQueue <String> (2, true );
By using this method, we can see that ArrayBlockingQueue supports ReentrantLock's fair lock mode and non-fair lock mode. For these two modes, check the article at the beginning of this article.
The source code is as follows:
Private final E [] items; // the underlying data structure private int takeIndex; // The index used for the next take/poll/remove (out of the queue) private int putIndex; // used for the next put/offer/add index (queued) private int count; // number of elements in the queue/** Concurrency control uses the classic two-condition algorithm found in any * textbook. * // ** Main lock guarding all access */private final ReentrantLock lock; // lock/** Condition for waiting takes */private final Condition notEmpty; // The waiting Condition/** Condition for waiting puts */private final Condition notFull; // The waiting ConditionView Code/*** create a queue, specify the queue capacity, specify the mode * @ param fair * true: the first thread operates first * false: random Order */public ArrayBlockingQueue (int capacity, boolean fair) {if (capacity <= 0) throw new IllegalArgumentException (); this. items = (E []) new Object [capacity]; // initialization class variable array items lock = new ReentrantLock (fair); // initialization class variable lock notEmpty = lock. newCondition (); // initialization class variable notEmpty Condition notFull = lock. newCondition (); // initialize the class variable notFull Condition}/*** to create a queue and specify the queue capacity, the default mode is non-fair mode * @ param capacity <1 will throw an exception */public ArrayBlockingQueue (int capacity) {this (capacity, false );}View Code
Note:
- Composition of ArrayBlockingQueue: an array of objects + 1 lock ReentrantLock + 2 Condition Conditions
- In the process of viewing the source code, we also need to imitate the use of conditional locks. This two-condition lock mode is a classic mode.
3. Join
3.1. public boolean offer (E e)
Principle:
- Insert an element at the end of the team. If the queue is not full, true is returned immediately. If the queue is full, false is returned immediately.
Usage:
Source code:
/*** Insert an element at the end of the team. * If the queue is not full, true is returned immediately. * If the queue is full, false is returned immediately. * Note: This method is generally better than add (), throwing an exception directly due to add () Failure */public boolean offer (E) {if (e = null) throw new NullPointerException (); final ReentrantLock lock = this. lock; lock. lock (); try {if (count = items. length) // return false when the array is full; else {// insert (e) When the array is not full; // insert an element return true ;}} finally {lock. unlock ();}}View Code private void insert (E x) {items [putIndex] = x; // insert element putIndex = inc (putIndex); // putIndex + 1 ++ count; // number of elements + 1/**** wake up a thread * If any thread is waiting for this condition, select a zone to wake up. * Before being awakened from the waiting state, the selected thread must obtain the lock again */notEmpty. signal ();}View Code/*** I + 1, array subscript + 1 */final int inc (int I) {return (++ I = items. length )? 0: I ;}View Code
The code is very simple. You only need to pay attention to the following points:
- After the element is inserted, wake up the thread waiting for the notEmpty condition (that is, to obtain the element). This is similar to the producer-consumer mode.
3.2. public boolean offer (E e, long timeout, TimeUnit unit) throws InterruptedException
Principle:
- Insert an element at the end of the team. If the array is full, wait until the following three conditions occur:
- Awakened
- Wait time timeout
- The current thread is interrupted.
Usage:
Try {abq. offer ("hello2", 1000, TimeUnit. MILLISECONDS);} catch (InterruptedException e) {e. printStackTrace ();}View Code
Source code:
/*** Insert an element at the end of the team. * If the array is full, wait until the following three conditions occur: * 1. Wake up * 2. Wait time out * 3. The current thread is interrupted */public boolean offer (E e, long timeout, TimeUnit unit) throws InterruptedException {if (e = null) throw new NullPointerException (); long nanos = unit. toNanos (timeout); // converts the timeout value to a nanosecond final ReentrantLock = this. lock;/** lockInterruptibly (): * 1. Obtain the lock if the current thread is not interrupted. * 2. if the result is obtained successfully, the method ends. * 3. If the lock cannot be obtained, the current thread is blocked until the following situation occurs: * 1) the current thread (after being awakened) successfully acquires the lock * 2) the current thread is interrupted by other threads ** lock () * to obtain the lock. If the lock cannot be obtained, the current thread is blocked until the lock can be obtained and obtained successfully. */Lock. lockInterruptibly (); // Add an interrupt lock try {for (;) {if (count! = Items. length) {// The queue is not full of insert (e); return true;} if (nanos <= 0) // The returned false has timed out; try {/** to wait: * Three things may occur in this process: * 1. Wake up --> continue the current for (;) loop * 2. Timeout --> continue the current (;;) loop * 3. interrupted --> and then directly execute the catch part code */nanos = notFull. awaitNanos (nanos); // wait (in this process, time will be lost, and the thread may also be awakened)} catch (InterruptedException ie) {// The thread is interrupted notFull while waiting. signal (); // wake up other thread throw ie;} finally {lock. unlock ();}}View Code
Note:
- AwaitNanos (nanos) is a method in AQS. It is not detailed here. If you are interested, check the source code of AQS.
- For the differences between lockInterruptibly () and lock (), see the notes.
3.3. public void put (E e) throws InterruptedException
Principle:
- Insert an element at the end of the team. If the queue is full, it will be blocked until the array is full or the thread is interrupted.
Usage:
Try {abq. put ("hello1");} catch (InterruptedException e) {e. printStackTrace ();}View Code
Source code:
/*** Insert an element at the end of the Team * If the queue is full, it will be blocked until the array is full or the thread is interrupted */public void put (E e) throws InterruptedException {if (e = null) throw new NullPointerException (); final E [] items = this. items; final ReentrantLock lock = this. lock; lock. lockInterruptibly (); try {while (count = items. length) // when the queue is full, it is always blocked here/** always waiting for the condition notFull, that is, it is awakened by other threads * (in fact, a thread leaves an element, then call notFull. signal () Wake up other threads waiting for this condition, and the queue is not slow.) */notFull. await ();} catch (InterruptedException ie) {// If notFull is interrupted. signal (); // wake up other threads waiting for this condition (notFull, namely, queuing) throw ie;} insert (e);} finally {lock. unlock ();}}View Code
4. Team-out
4.1. public E poll ()
Principle:
- If no element exists, null is directly returned. If an element exists, the Header element is set to null. However, note that the header is changed at any time, not always items [0].
Usage:
Abq. poll ();
Source code:
/*** Team out */public E poll () {final ReentrantLock = this. lock; lock. lock (); try {if (count = 0) // if no element exists, null is directly returned instead of returning null; E x = extract (); return x;} finally {lock. unlock ();}}View Code/*** team out */private E extract () {final E [] items = this. items; E x = items [takeIndex]; // gets the team element items [takeIndex] = null; // leave the field of the element to the left blank. ** the takeIndex of the first team member is equal to or equal to 0, and the takeIndex of the second team member is equal to or equal to 1 *. (Note: after the team member leaves, the following array elements are not moved forward.) */takeIndex = inc (takeIndex); -- count; // Number of array elements-1 notFull. signal (); // The array is no longer satisfied. Wake up other threads waiting for the notFull condition to return x; // return the element of the queue}View Code
4.2. public E poll (long timeout, TimeUnit unit) throws InterruptedException
Principle:
- Delete an element from the first element. If the array is not empty, it is out of the queue. If the array is empty and time-out, null is returned. If the array is empty and time-out, wait until the following three conditions occur:
- Awakened
- Wait time timeout
- The current thread is interrupted.
Usage:
Try {abq. poll (1000, TimeUnit. MILLISECONDS);} catch (InterruptedException e) {e. printStackTrace ();}View Code
Source code:
/*** Delete an element from the first pair. * If the array is not empty, it leaves the queue. * If the array is empty, determine whether the time has timed out. If the array has timed out, return null * If the array is empty and the time has not timed out, wait until the following three conditions occur: * 1. Wake up * 2. Wait time timeout * 3. The current thread is interrupted */public E poll (long timeout, TimeUnit unit) throws InterruptedException {long nanos = unit. toNanos (timeout); // converts the time to a nanosecond final ReentrantLock = this. lock; lock. lockInterruptibly (); try {for (;) {if (count! = 0) {// The array is not empty. E x = extract (); // return x;} if (nanos <= 0) // return null upon time-out; try {/** wait: * Three things may occur in this process: * 1. Wake up --> continue the current (;;) loop * 2. Timeout --> continue the current for (;) loop * 3. be interrupted --> then directly execute the catch part of the Code */nanos = notEmpty. awaitNanos (nanos);} catch (InterruptedException ie) {notEmpty. signal (); // propagate to non-interrupted thread throw ie ;}} finally {lock. unlock ();}}View Code
4.3. public E take () throws InterruptedException
Principle:
- Leave the queue Header element. If the queue is empty, it will be blocked until the array is not empty or the thread is interrupted.
Usage:
Try {abq. take ();} catch (InterruptedException e) {e. printStackTrace ();}View Code
Source code:
/*** Leave the queue Header element * If the queue is empty, it will be blocked until the array is not empty or the thread is interrupted */public E take () throws InterruptedException {final ReentrantLock lock = this. lock; lock. lockInterruptibly (); try {while (count = 0) // If the array is empty, it will always be blocked here/** keep waiting for the condition notEmpty, that is, it is awakened by other threads * (in fact, a thread queues an element and then calls notEmpty. signal () Wake up other threads waiting for this condition, and the queue is not empty.) */notEmpty. await ();} catch (InterruptedException ie) {notEmpty. signal (); // propagate to non-interrupted thread throw ie;} E x = extract (); return x;} finally {lock. unlock ();}}View Code
Summary:
1. Schematic diagram of the specific teams and teams: Here is only one case. See the dark part on the way to indicate that there are existing elements, and the light part has no elements.
How is the above situation formed? When the queue is full, the first element of the queue is items [0]. This is the case above.
If you want to leave the team again now, the element in the Team's head is items [1]. After you leave the team, the following situation is formed.
After leaving the queue, the first element is items [2]. Assuming that an element is about to be added to the queue, we can see from the inc method that it will be inserted into items [0, the formation of the team:
The above is the entire team-out process. The inc method has been provided above, And I will post it again here:
/*** I + 1, array subscript + 1 * Note: the reason for this write is as follows. */Final int inc (int I) {return (++ I = items. length )? 0: I ;}View Code
2. Comparison of Three Types of teams:
- Offer (E): If the queue is not full, true is returned immediately; if the queue is full, false is returned immediately --> not blocked
- Put (E): If the queue is full, it is blocked until the array is full or the thread is interrupted. --> Blocking
- Offer (E e, long timeout, TimeUnit): insert an element at the end of the team. If the array is full, wait until the following three conditions occur: --> Blocking
- Awakened
- Wait time timeout
- The current thread is interrupted.
3. Comparison of three types:
- Poll (): If no element exists, null is directly returned. If an element exists
- Take (): If the queue is empty, it will be blocked until the array is not empty or the thread is interrupted --> Blocking
- Poll (long timeout, TimeUnit unit, until the following three situations occur:
- Awakened
- Wait time timeout
- The current thread is interrupted.