并发编程7-ArrayBlockingQueue底层实现和原理

ArrayBlockingQueue的原理和底层实现的数据结构 :

ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列,可以按照 FIFO(先进先出)原则对元素进行排序。

线程安全是指,ArrayBlockingQueue内部通过“互斥锁”保护竞争资源,实现了多线程对竞争资源的互斥访问。而有界,则是指ArrayBlockingQueue对应的数组是有界限的。 阻塞队列,是指多线程访问竞争资源时,当竞争资源已被某线程获取时,其它要获取该资源的线程需要阻塞等待;所谓公平的访问队列是指阻塞的线程,可以按照阻塞的先后顺序访问队列,先阻塞的线程先访问ArrayBlockingQueue队列。非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格,有可能先阻塞的线程最后才能够访问队列。然而为了保证公平性,通常会降低吞吐量。

1.ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。 并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。

2.ArrayBlockingQueue内部通过Object[]数组保存数据的,也就是说ArrayBlockingQueue本质上是通过数组实现的。ArrayBlockingQueue的大小,即数组的容量是在创建创建ArrayBlockingQueue时候指定的。

3.如下图所示,ArrayBlockingQueue和ReentrantLock是组合关系,ArrayBlockingQueue中包含一个ReentrantLock对象。ReentrantLock是可重入的互斥锁。ArrayBlockingQueue就是根据ReentrantLock互斥锁实现"多线程对共享资源的访问"。ReentrantLock分为公平锁和非公平锁,关于具体使用公平锁还是非公平锁,在创建ArrayBlockingQueue时可以指定;而且,ArrayBlockingQueue默认会使用非公平锁。

4.ArrayBlockingQueue和Condition是组合关系,ArrayBlockingQueue中包含两个Condition对象(notEmpty和notFull)。使用通知模式实现:所谓通知模式,当生产者往满的队列里面添加元素的时候,会阻塞生产者(调用Condition notFull.await()进行等待);当消费者消费了一个队列中的元素后,会通知(调用Condition notFull.signal()唤醒生产者)生产者当前队列可用。反之,当消费者消费的时候,发现队列是空的,则消费者会被阻塞(通过Condition的 notEmpty.await()进行等待),当生产者插入了队列中的一个元素后,则会调用notEmpty.signal()唤醒消费者继续消费。

关于CONDITION,可以参考《并发编程5-AQS的Condition实现原理》

ArrayBlockingQueue的数据结构如下:

并发编程7-ArrayBlockingQueue底层实现和原理_第1张图片

ArrayBlockingQueue方法列表:
// 创建一个带有给定的(固定)容量和默认访问策略的 ArrayBlockingQueue。
ArrayBlockingQueue(int capacity)
// 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue。
ArrayBlockingQueue(int capacity, boolean fair)
// 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue,它最初包含给定 collection 的元素,并以 collection 迭代器的遍历顺序添加元素。
ArrayBlockingQueue(int capacity, boolean fair, Collection c)

// 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException。
boolean add(E e)
// 自动移除此队列中的所有元素。
void clear()
// 如果此队列包含指定的元素,则返回 true。
boolean contains(Object o)
// 移除此队列中所有可用的元素,并将它们添加到给定 collection 中。
int drainTo(Collection c)
// 最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中。
int drainTo(Collection c, int maxElements)
// 返回在此队列中的元素上按适当顺序进行迭代的迭代器。
Iterator iterator()
// 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false。
boolean offer(E e)
// 将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间。
boolean offer(E e, long timeout, TimeUnit unit)
// 获取但不移除此队列的头;如果此队列为空,则返回 null。
E peek()
// 获取并移除此队列的头,如果此队列为空,则返回 null。
E poll()
// 获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。
E poll(long timeout, TimeUnit unit)
// 将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。
void put(E e)
// 返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的其他元素数量。
int remainingCapacity()
// 从此队列中移除指定元素的单个实例(如果存在)。
boolean remove(Object o)
// 返回此队列中元素的数量。
int size()
// 获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
E take()
// 返回一个按适当顺序包含此队列中所有元素的数组。
Object[] toArray()
// 返回一个按适当顺序包含此队列中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
 T[] toArray(T[] a)
// 返回此 collection 的字符串表示形式。
String toString()

下面从ArrayBlockingQueue的创建,添加,取出,遍历这几个方面对ArrayBlockingQueue进行分析。

1.创建:

 /**
     * Creates an {@code ArrayBlockingQueue} with the given (fixed)
     * capacity and default access policy.
     *
     * @param capacity the capacity of this queue
     * @throws IllegalArgumentException if {@code capacity < 1}
       只是指定ArrayBlockingQueue的容量,默认采用非公平互斥锁
*/ public ArrayBlockingQueue(int capacity) { this(capacity, false); }

/**
     * Creates an {@code ArrayBlockingQueue} with the given (fixed)
     * capacity and the specified access policy.
     *
     * @param capacity the capacity of this queue
     * @param fair if {@code true} then queue accesses for threads blocked
     *        on insertion or removal, are processed in FIFO order;
     *        if {@code false} the access order is unspecified.
     * @throws IllegalArgumentException if {@code capacity < 1}
       指定容量和ReetrantLock的类型是否为公平锁创建阻塞队列
     */
    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();
    }

说明:
items是保存“阻塞队列”数据的数组。它的定义如下:

    final Object[] items;

fair是“可重入的独占锁(ReentrantLock)”的类型。fair为true,表示是公平锁;fair为false,表示是非公平锁。notEmpty和notFull是锁的两个Condition条件。它们的定义如下:

final ReentrantLock lock;
 
private final Condition notEmpty;
  
private final Condition notFull;

说明:
Lock的作用是提供独占锁机制,来保护竞争的资源;condition提供了类似object.wait和notify的线程通信机制,但是condition支持多个等待队列,使用上更加灵活
notEmpty表示"锁的非空条件"。当某线程想从队列中获取数据的时候,而此时队列中的数据为空,则该线程通过notEmpty.await()方法进行等待;

当其他线程向队列中插入元素之后,就调用notEmpty.signal()方法进行唤醒之前等待的线程。同理,notFull表示“锁满的条件“。当某个线程向队列中插入元素,而此时队列已满时,该线程等待,即阻塞通过notFull.wait()方法;其他线程从队列中取出元素之后,就唤醒该等待的线程,这个线程调用notFull.signal()方法。
2.添加:

/**
     * Inserts the specified element at the tail of this queue, waiting
     * for space to become available if the queue is full.
     *
     * @throws InterruptedException {@inheritDoc}
     * @throws NullPointerException {@inheritDoc}
       添加元素,当队列满的时候,该线程等待,即阻塞。
     */
    public void put(E e) throws InterruptedException {
        //校验插入的元素不能为null
        checkNotNull(e);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            //队列满的时候
            while (count == items.length)
                //线程调用await方法阻塞
                notFull.await();
            //否则插入数据
            insert(e);
        } finally {
            lock.unlock();
        }
    }

  /** 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;

/**
     * Inserts element at current put position, advances, and signals.
     * Call only when holding lock.
     */
    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        //队列中的元素个数
        ++count;
        //唤醒notEmpty Condition锁上面等待的线程,告诉该线程队列不为空了,可以消费了
        notEmpty.signal();
    }

/**
     * Circularly increment i.
       队列中的元素个数==队列的长度的时候,队列满,则设置下一个被添加元素的索引为0
     */
    final int inc(int i) {
        return (++i == items.length) ? 0 : i;
    }

2.取出:

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        //获取锁,如果当前线程是中断状态,则抛出interruptedException异常
        lock.lockInterruptibly();
        try {
            //队列为空的时候,则线程一直阻塞等待
            while (count == 0)
                notEmpty.await();
            //否则取出元素
            return extract();
        } finally {
            lock.unlock();
        }
    }

/**
     * Extracts element at current take position, advances, and signals.
     * Call only when holding lock.
     */
    private E extract() {
        final Object[] items = this.items;
        //强制将元素转化为"泛型E"
        E x = this.cast(items[takeIndex]);
        items[takeIndex] = null;
        //设置下一个被取出元素的索引
        takeIndex = inc(takeIndex);
        //将队列中的元素-1
        --count;
        //唤醒notFull条件上面等待的线程,告诉该线程队列不是满的了,可以添加元素了
        notFull.signal();
        return x;
    }

你可能感兴趣的:(并发相关)