1. Introduction of ArrayBlockingQueue

Is an array implementation of the ring queue, often use concurrent containers for storage of shared data between multiple threads, which can not only ensure thread safety, but also simplify the operation of each thread

1. How ArrayBlockingQueue works

The Condition notification mechanism of Lock Lock is used for blocking control.

Core: one lock, two conditions

// Final Object[] items; Int takeIndex; Int putIndex; // Number of elements int count; // Internal lock final ReentrantLock lock; Private final Condition notEmpty; // Private final Condition notFull; public ArrayBlockingQueue(int capacity, boolean fair) { ... lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); }Copy the code

2. Understand put and take source code

2.1 put summary

Public void put(E E) throws InterruptedException {// Check whether it is null checkNotNull(E); final ReentrantLock lock = this.lock; // Get the optional lock lock.lockInterruptibly(); While (count == items.length) notfull.await (); // Enter the blocking queue enqueue(e); } finally { lock.unlock(); } } private void enqueue(E x) { // assert lock.getHoldCount() == 1; // assert items[putIndex] == null; final Object[] items = this.items; // Put the value in the queue items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; // Wake up the producer notempty.signal (); }Copy the code

We are making a text summary of the flow chart

  1. The thread that holds the lock contention goes to the next step, and the thread that does not hold the lock contention spins.
  2. Determines whether the blocking queue is full, and if it is, blocks the thread by calling the await method, the notFull (producer) suspends, and finally releases the lock to wait to be woken up by the consumer thread.
  3. If not, the enqueue method is called to put the element into the blocking queue.
  4. Wakes up a thread labeled notEmpty (consumer).

2.2 take summary

public E take() throws InterruptedException { final ReentrantLock lock = this.lock; // Optionally obtain the lock lock.lockInterruptibly(); Try {// If the queue is empty, the consumer suspectswhile (count == 0) notempty.await (); Return dequeue(); } finally { lock.unlock(); } } private E dequeue() { // assert lock.getHoldCount() == 1; // assert items[takeIndex] ! = null; final Object[] items = this.items; E x = (E) items[takeIndex]; items[takeIndex] = null; if (++takeIndex == items.length) takeIndex = 0; count--; if (itrs ! = null) itrs.elementDequeued(); // Wake up the producer notfull.signal (); return x; }Copy the code

We are making a text summary of the flow chart

  1. The thread that holds the lock contention goes to the next step, and the thread that does not hold the lock contention spins.
  2. Determine if the blocking queue is empty, and if it is empty, block the thread by calling await method, notEmpty (consumer) hangs, and finally release lock lock, waiting to be awakened by the producer thread.
  3. If there is none, the dequeue method is called.
  4. Wakes up a thread marked notFull (producer)