In the previous article, we talked about the LinkedBlockingQueue in the concurrent queue. This time, let's look at the ArrayBlockingQueue and its name. Let's imagine the difference between LinkedList and ArrayList. We can see that the bottom layer of ArrayBlockingQueue must be implemented based on array, which is a bounded array;
The components of ArrayBlockingQueue are similar to LinkedBlockingQueue, but also have two conditional variables: maintain the blocking queue and implement the production consumer mode;
1, Simple understanding of ArrayBlockingQueue
Let's take a look at some common attributes:
//Array to hold queue elements final Object[] items; //Exit index int takeIndex; //Entry index int putIndex; //Number of elements in the queue int count; //Exclusive lock final ReentrantLock lock; //If the array is empty and the thread fetches data, it will be put into this condition variable to block private final Condition notEmpty; //When the queue is full, and threads add data to the array, the threads are dropped here to block private final Condition notFull;
Since this is a bounded array, let's look at the constructor again:
//Specify capacity, default is unfair policy public ArrayBlockingQueue(int capacity) { this(capacity, false); } //Specify capacity and exclusive lock policy public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = new Object[capacity]; lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); } //You can specify capacity, lock policy, and initialization data public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) { this(capacity, fair); final ReentrantLock lock = this.lock; lock.lock(); // Lock only for visibility, not mutual exclusion try { int i = 0; try { for (E e : c) { checkNotNull(e); items[i++] = e; } } catch (ArrayIndexOutOfBoundsException ex) { throw new IllegalArgumentException(); } count = i; putIndex = (i == capacity) ? 0 : i; } finally { lock.unlock(); } }
2, offer method
Add an element to the end of the queue. If it is added successfully, it will return true. If the queue is full, it will lose the current element and return false directly. The method is not blocked;
public boolean offer(E e) { //Non empty test checkNotNull(e); final ReentrantLock lock = this.lock; lock.lock(); try { //If the actual quantity and the maximum capacity in the array are equal, adding fails, return false if (count == items.length) return false; else { //Successfully added. The method is implemented as follows enqueue(e); return true; } } finally { //Release lock lock.unlock(); } } private void enqueue(E x) { //Get the array final Object[] items = this.items; //stay putIndex Put data in this location x,Then put putIndex Add one to indicate that this parameter represents the index of the next data location items[putIndex] = x; //Here putIndex For example, if the maximum capacity of an array is 5, then the maximum index value should be 4. If putIndex Equal to 5, indicating array //Out of bounds, reset this index to 0 if (++putIndex == items.length) putIndex = 0; count++; //After adding, it indicates that there is data in the array. Here, it wakes up the thread blocked by fetching data from the array notEmpty.signal(); }
3, put method
Insert an element at the end of the queue. If the queue is idle, it will return true successfully. If the queue is full, it will block the current thread to the condition queue of notFull. If there is idle, it will wake up. If there is any interrupt during the blocking process, it will respond to it;
public void put(E e) throws InterruptedException { //Non empty check checkNotNull(e); final ReentrantLock lock = this.lock; //Note how the lock is acquired lock.lockInterruptibly(); try { //If the thread is full, put the current thread in the notFull In the blocking queue of condition variable while (count == items.length) notFull.await(); //Add data if not full enqueue(e); } finally { //Release lock lock.unlock(); } }
4, poll method
The header gets and removes an element. If the queue is empty, null is returned, and the method is not blocked;
public E poll() { final ReentrantLock lock = this.lock; lock.lock(); try { //If the queue is empty, return null //If the queue is not empty, call dequeue Method to get and delete the elements in the queue header return (count == 0) ? null : dequeue(); } finally { lock.unlock(); } } private E dequeue() { //Get array final Object[] items = this.items; @SuppressWarnings("unchecked") //Obtain takeIndex The element of the location, which will return E x = (E) items[takeIndex]; //Then will takeInde Position set to empty items[takeIndex] = null; //If takeIndex It's the last position of the array, so takeIndex Reset to 0 if (++takeIndex == items.length) takeIndex = 0; //Actual quantity minus one count--; if (itrs != null) itrs.elementDequeued(); //awaken notFull Middle thread notFull.signal(); return x; }
5, take method
Get and delete the element in the head of the current queue. If the queue is empty and the current thread is blocked until it is awakened, it will respond to the interrupt;
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; //Obtain locks in an interruptible way lock.lockInterruptibly(); try { //If the array is empty, wake up notEmpty Threads in the condition queue in while (count == 0) notEmpty.await(); //Get and delete the header node return dequeue(); } finally { lock.unlock(); } }
6, peek method
Just get the header element and do not delete it. If the queue is empty, null will be returned. This method is thread free
public E peek() { final ReentrantLock lock = this.lock; lock.lock(); try { return itemAt(takeIndex); // null when queue is empty } finally { lock.unlock(); } } //Get the index in the array as takeIndex Data in @SuppressWarnings("unchecked") final E itemAt(int i) { return (E) items[i]; }
Seven. Conclusion
After understanding the LinkedBlockingQueue mentioned in the previous blog, it's actually too easy to read this one again, which is to operate the array! It is shown in the following figure: