Have you understood ArrayBlockingQueue?

Overview

Since java5, jdk has added the concurrent package, BlockingQueue in concurrent, which is the blocking queue, BlockingQueue is just an interface, and jdk provides rich implementation classes for different scenarios. This article is about ArrayBlockingQueue.

ArrayBlockingQueue简介

ArrayBlockingQueue inherits the AbstractQueue class and implements the BlockingQueue interface. It is an array-based bounded queue. The lock is implemented based on ReentrantLock. There is only one lock object, which leads to a shared lock for enqueue/dequeue and cannot achieve parallel access.

application

1. In practical applications, the amount of data should be fully considered when setting the array size. If the set value is too small, it will easily cause blockage, and the size cannot be modified during operation.
2. Null element is not supported, otherwise NullPointerException will be reported.

Main method

Insert picture description here
1. Insert data
(1) offer(E e): If the queue is not full, return true, if the queue is full, return false (not blocked).
(2) offer (E e, long timeout, TimeUnit unit): You can set the waiting time, if the queue is full, wait. If the waiting time is exceeded, false is returned.
(3) put (E e): No return value, wait until the queue vacates a place.

2. Get data
(1) poll(): If there is data, leave the team, if there is no data, return null.
(2) poll (long timeout, TimeUnit unit): the waiting time can be set, if there is no data, then wait, if the waiting time is exceeded, then null will be returned.
(3) take(): If there is data, leave the team. If there is no data, keep waiting (blocking).

Source code analysis

1.AbstractQueue

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable

Inherit the AbstractQueue abstract class and implement the BlockingQueue interface.

2. Member variables

final Object[] items;//队列

    /** 下次出队下标(take、poll、remove) */
    int takeIndex;

    /** 下次入队下标(pull、offer、add) */
    int putIndex;

    /** 队列中元素个数 */
    int count;


    /** 锁 */
    final ReentrantLock lock;

    /** 等待条件(出队) */
    private final Condition notEmpty;

    /** 等待条件(入队) */
    private final Condition notFull;

    /**
     * 迭代器
     */
    transient Itrs itrs = null;

As can be seen from the source code, ArrayBlockingQueue is an array-based queue, and the lock is based on ReentrantLock.

3. Constructor

	public ArrayBlockingQueue(int capacity) {
    
    
        this(capacity, false);
    }
 
    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();
    }

From the source code, the array length must be set to instantiate ArrayBlockingQueue, which is why ArrayBlockingQueue is a bounded queue

4.offer

	public boolean offer(E e) {
    
    
        //判断你是否为null,为null抛NullPointerException
        checkNotNull(e);
        //获取锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            //如果队列已满,返回false
            if (count == items.length)
                return false;
            else {
    
    
                //入队,返回true
                enqueue(e);
                return true;
            }
        } finally {
    
    
            //释放锁
            lock.unlock();
        }
    }
    
    public boolean offer(E e, long timeout, TimeUnit unit)
            throws InterruptedException {
    
    
        //判断是否为null,为null抛NullPointerException
        checkNotNull(e);

        long nanos = unit.toNanos(timeout);
        //获取锁
        final ReentrantLock lock = this.lock;
        //lockInterruptibly优先考虑响应中断
        lock.lockInterruptibly();
        try {
    
    
            //如果队列已满
            while (count == items.length) {
    
    
                //超时直接返回false
                if (nanos <= 0)
                    return false;
                //生产线程堵塞nanos时间,也有可能被唤醒,如果超过nanos时间还未被唤醒,则nanos=0,再次循环,就会返回false
                nanos = notFull.awaitNanos(nanos);
            }
            //设置元素
            enqueue(e);
            return true;
        } finally {
    
    
            //释放锁
            lock.unlock();
        }
    }
    
    private void enqueue(E x) {
    
    
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        //获取数组
        final Object[] items = this.items;
        //向入队下标数组设置元素
        items[putIndex] = x;
        //如果入队下标已经是最后一个,则证明队列已满,入队下标设置为0
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        //唤醒堵塞的消费线程
        notEmpty.signal();
    }

It can be seen from the source code that why the offer method is set to null will throw an error, because the first code is the checkNotNull method. If the queue is full, return false, and if the queue is not full, return true.
The difference between the two offer methods is whether there is a waiting time, and the implementation is basically the same.

5.put

	public void put(E e) throws InterruptedException {
    
    
        //判断是否为null,为null抛NullPointerException
        checkNotNull(e);
        //获取锁
        final ReentrantLock lock = this.lock;
        //lockInterruptibly优先考虑响应中断
        lock.lockInterruptibly();
        try {
    
    
            //如果队列已满
            while (count == items.length)
                //一直堵塞,直至被唤醒
                notFull.await();
            //设置元素
            enqueue(e);
        } finally {
    
    
            //释放锁
            lock.unlock();
        }
    }

It can be seen from the source code that the difference between the put and offer methods is basically the same, but the put method passes through notFull.await(), which is blocked until it is awakened.

6.poll

	public E poll() {
    
    
        //获取锁
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
    
    
            //如果队列为空,返回null,否则返回dequeue方法获取的值
            return (count == 0) ? null : dequeue();
        } finally {
    
    
            //释放锁
            lock.unlock();
        }
    }

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    
    
        //获取等待时间
        long nanos = unit.toNanos(timeout);
        //获取锁
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
    
    
            //如果队列为空
            while (count == 0) {
    
    
                //当超时后,直接返回null
                if (nanos <= 0)
                    return null;
                //将线程堵塞nanos时间,堵塞时间内可能被唤醒,超过nanos时间,nanos=0,下次循环返回null
                nanos = notEmpty.awaitNanos(nanos);
            }
            //获取元素
            return dequeue();
        } finally {
    
    
            //释放锁
            lock.unlock();
        }
    }

    private E dequeue() {
    
    
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;

        //通过出队下标获取元素
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        //如果出队是数组中最后一个,下一个出队从0开始
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        //唤醒生产的线程
        notFull.signal();
        return x;
    }

As can be seen from the source code, why the poll method returns null if it fails to get the data. The difference between the two polls is whether there is a block time, and the others are basically the same.

7.take

public E take() throws InterruptedException {
    
    
        //获取锁
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
    
    
            //如果队列为空,则堵塞,直至被唤醒
            while (count == 0)
                notEmpty.await();
            //获取元素
            return dequeue();
        } finally {
    
    
            //释放锁
            lock.unlock();
        }
    }

It can be seen from the source code that take is very simple. The core logic is that the queue is empty and waits until it is awakened.

Guess you like

Origin blog.csdn.net/qq_38306425/article/details/109352954
Recommended