ArrayBlockingQueue源码分析

ArrayBlockingQueue源码分析,ArrayBlockingQueue实现了哪些接口?
public class ArrayBlockingQueue extends AbstractQueue
implements BlockingQueue, java.io.Serializable
直接实现了BlockingQueue、Serializable接口,继承了AbstractQueue类,AbstractQueue继承了AbstractCollection抽象类,从而拥有了部分集合的功能。

ArrayBlockingQueue主要变量分析:
E[] items:元素存放在此数组当中,也就是说此类的数据缓存使用的数据结构是数组,由类名也可以看出。
int takeIndex:记录目前取到了数组中的第几个元素的下标
int putIndex:要在数组中插入元素的位置下标,插入一个元素,值加1,当值等于数组长度时,值重置为0,达到循环利用数组的作用。
int count:队列中目前有多少个元素
ReentrantLock lock:此类之所以是线程安全的,是因为使用了锁,也就是这个变量
Condition notEmpty:当数组中没有元素,调用take方法阻塞后,就是通过此条件唤醒的
Condition notFull: 当数组中元素已满调用put方法阻塞后,通过此条件唤醒的

看完了主要变量,还是从构造方法开始这个类的方法分析。
构造方法有两个,一个入参和两个入参的,直接看两个入参的,因为一个入参的底层也是会调用到两个入参的。

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = (E[]) new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

构造方法中:(1):初始化了数组的大小;(2)创建了一个锁对象,这个锁默认是不公平锁(唤醒线程时不按照等待时间长短获得所,按照先抢到谁先获得锁);(3)创建两个条件类。

这个类有两大类操作,放元素和取元素。每类操作又有阻塞操作和非阻塞操作。
非阻塞放元素的方法有:add()和offer(),两个方法的区别是add()放元素时,若队列已满,则会抛异常,而offer()方法则会返回false。
非阻塞取元素的方法有:poll()
阻塞放元素的方法有:put();
阻塞取元素的方法有:take();
接下来我们主要分析下阻塞放、取元素,这两个方法明白了之后,其他的方法都很容易理解。
先看阻塞放元素的put方法源码:

public void put(E e) throws InterruptedException {
    if (e == null) throw new NullPointerException();
    final E[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        try {
            while (count == items.length)
                notFull.await();
        } catch (InterruptedException ie) {
            notFull.signal(); // propagate to non-interrupted thread
            throw ie;
        }
        insert(e);
    } finally {
        lock.unlock();
    }
}

首先判断队列中的元素个数是否达到了初始化数组的大小,若达到了,说明队列满了,则阻塞等待;否则,执行insert方法插入元素。insert方法源码:

private void insert(E x) {
    items[putIndex] = x;
    putIndex = inc(putIndex);
    ++count;
    notEmpty.signal();
}
final int inc(int i) {
    return (++i == items.length)? 0 : i;
}

insert主要的工作有:(1)在putIndex下标处插入元素;(2)putIndex加1,若达到数组长度,则重置为0;(3)count加1;(4)唤醒执行take方法时阻塞的线程

阻塞取元素方法take方法:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        try {
            while (count == 0)
                notEmpty.await();
        } catch (InterruptedException ie) {
            notEmpty.signal(); // propagate to non-interrupted thread
            throw ie;
        }
        E x = extract();
        return x;
    } finally {
        lock.unlock();
    }
}

首先判断队列中元素是否为0,如果为0,则调用notEmpty.await()阻塞,直到队列中有元素的时候被notEmpty.signal()唤醒;如果队列中元素不为0,则调用extract()方法获取元素。extract()源码:

private E extract() {
    final E[] items = this.items;
    E x = items[takeIndex];
    items[takeIndex] = null;
    takeIndex = inc(takeIndex);
    --count;
    notFull.signal();
    return x;
}

extract()方法主要做的事情有:(1)根据takeIndex记录的下标从数组中取元素,最开始时值为0;(2)取到元素后将对应的位置置空;(3)takeIndex值加1,若达到数组长度,值重置为0,重新从0开始取,跟putIndex用法一样;(4)count值减1;(5)唤醒执行put方法时阻塞的线程

总结:ArrayBlockingQueue从名字可以看出来,底层存元素的结构是数组,有阻塞功能,阻塞功能是通过两个Condition进行协调来实现生产消费模式的。因为读取元素之前又会使用ReentrantLock进行同步,所以它又是线程安全的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值