JAVA源码学习之集合-ArrayBlockingQueue

ArrayBlockingQueue是一个基于数组的有界阻塞队列,使用ReentrantLock实现线程安全。它通过putIndex和takeIndex控制FIFO顺序,当队列满或空时进行阻塞。队列满时,offer操作会阻塞;队列空时,take和poll操作会阻塞。公平策略可选,确保线程调度的公平性。此外,还支持peek和remove等操作,用于检查或移除队首元素。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

从这章开始我们就正式的进入到了BolckingQueue的篇章

正文

类的描述

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

通过数组实现的一个有界blockingQueue。这个队列的命令元素的FIFO(先进先出)。队列的头是已在队列中的元素,是最长的时间。队列的尾部是已在队列中的元素最短的时间。新的元素插入在队列的尾部,并在队列的头部获取元素的队列检索操作。

这是一个经典的“有界缓冲区”,其中一个固定大小的数组保存生产者插入的元素和消费者提取的元素。一旦创建,容量就无法更改。尝试将元素放入完整队列将导致操作阻塞;尝试从空队列中获取元素也会被阻塞。

此类支持一个可选的公平策略,用于排序等待的生产者线程和消费者线程。默认情况下,不保证此顺序。然而,公平性设置为true的队列以FIFO顺序授予线程访问权限。公平性通常会降低吞吐量,但会降低可变性并避免饥饿。

这个类和它的迭代器实现所有的可选方法的CollectionIterator接口。

常量、变量、静态内部类

 final Object[] items;

    /** items index for next take, poll, peek or remove */
    int takeIndex;

    /** items index for next put, offer, or add */
    int putIndex;

    /** Number of elements in the queue */
    int count;

    /*
     * Concurrency control uses the classic two-condition algorithm
     * found in any textbook.
     */

    /** Main lock guarding all access */
    final ReentrantLock lock;

    /** tasks的条件*/
    private final Condition notEmpty;

    /** put的条件 */
    private final Condition notFull;

    /**
     * Shared state for currently active iterators, or null if there
     * are known not to be any.  Allows queue operations to update
     * iterator state.
     */
    transient Itrs itrs = null;

通过上面变量,和类的描, 猜测是通过items数组存储元素的,通过ReentrantLock来实现阻塞的

构造方法

public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

   // fair ReentranLock是否是公平锁
   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();
    }


 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();
        }
    }

队列中常用的方法

add

public boolean add(E e) {
        return super.add(e); //父类的add
    }

//abstractQueue
 public boolean add(E e) {
        if (offer(e)) //调用的offer
            return true;
        else
            throw new IllegalStateException("Queue full");
    }

offer

  public boolean offer(E e) {
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length) //超出了队列初始化的长度
                return false;
            else {
                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;
        if (++putIndex == items.length)
            putIndex = 0;
        count++;
        notEmpty.signal(); //task从等待队列中移到同步队列,具体放到lock中学习
    }

remove

 public boolean remove(Object o) {
        if (o == null) return false;
        final Object[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count > 0) {
                final int putIndex = this.putIndex;
                int i = takeIndex;
                do {
                    if (o.equals(items[i])) {
                        removeAt(i);
                        return true;
                    }
                    if (++i == items.length)
                        i = 0;
                } while (i != putIndex);
            }
            return false;
        } finally {
            lock.unlock();
        }
    }


void removeAt(final int removeIndex) {
        // assert lock.getHoldCount() == 1;
        // assert items[removeIndex] != null;
        // assert removeIndex >= 0 && removeIndex < items.length;
        final Object[] items = this.items;
        if (removeIndex == takeIndex) {
            // removing front item; just advance
            items[takeIndex] = null;
            if (++takeIndex == items.length)
                takeIndex = 0;
            count--;
            if (itrs != null)
                itrs.elementDequeued();
        } else {
            // an "interior" remove

            // slide over all others up through putIndex.
            //通过putIndex相上移动元素
            final int putIndex = this.putIndex;
            for (int i = removeIndex;;) {
                int next = i + 1;
                if (next == items.length)
                    next = 0;
                if (next != putIndex) {
                    items[i] = items[next];
                    i = next;
                } else {
                    items[i] = null;
                    this.putIndex = i;
                    break;
                }
            }
            count--;
            if (itrs != null)
                itrs.removedAt(removeIndex);
        }
        notFull.signal();
    }

 //AbstractQueueRemove
 public E remove() {
        E x = poll();
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

poll 

 public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : 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;
        if (++takeIndex == items.length)
            takeIndex = 0;
        count--;
        if (itrs != null)
            itrs.elementDequeued();
        notFull.signal();
        return x;
    }

peek

 public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return itemAt(takeIndex); // null when queue is empty
        } finally {
            lock.unlock();
        }
    }
final E itemAt(int i) {
        return (E) items[i];
    }

element

   public E element() {
        E x = peek(); //调用的peek
        if (x != null)
            return x;
        else
            throw new NoSuchElementException();
    }

结论:

根据上面的方法,发现一个很有意识的东西,虽然存储元素是用的item数组,但是先进先出的规则是用的putIndex和taskIndex来实现的,新添加一条数据, putIndex +1 , 新移除一条数据 taskIndex + 1 当 putIndex == item.lenth 时, putIndex =0,从数组的第一个元素算起,taskIndex同理,这样其实数据结构就是一个环的形状

有兴趣的同学可以通过这段代码来操作下

public class ArrayBlockingQueueDemo {
	public static void main(String[] args) {
		ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue<>(3); // putIndex = 0 taskIndex = 0
		arrayBlockingQueue.add("AAA"); // putIndex = 1 taskIndex = 0
		arrayBlockingQueue.add("BBB"); // putIndex = 2 taskIndex = 0
		arrayBlockingQueue.add("CCC"); // putIndex = 0 taskIndex = 0
        arrayBlockingQueue.remove();   // putIndex = 0 taskIndex = 1
		arrayBlockingQueue.add("DDD"); // putIndex = 1 taskIndex = 1
	}
}

当走完最后一步的时候,item里面的数据存储, ”DDD“ 在 item 0的位置,所以把他形容成环

1.  ArrayBlockingQueue 是一个有界队列

2. 通过数组来保存数据,但是通过taskIndex和putIndex来控制先进先出,数据结构实际是一个环

3, 通过ReentrantLock来实现的阻塞和线程安全

上一章                                                                                                                                     下一章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值