ArrayBlockingQueue 是一个基于数组、先进先出、线程安全的集合类,其特色为可实现指定时间的阻塞读写,并且容器是可限制的,其具体实现如下。
一、ArrayBlockingQueue(int)
没有默认构造器,传入的参数即为创建的对象数组的大小,同时初始化锁和两个锁上的 Condition,一个为 notEmpty,一个为 notFull。
二、offer(E,long,TimeUnit)
此方法用于插入元素至数组的尾部,如数组已满,则进入等待,直到出现以下三种情况时才继续:被唤醒、到达指定的时间或当前线程被中断(interrupt),来看看具体实现。
此方法首先将指定的时间转换为纳秒,然后加锁,如数组未满,则将对象插入数组,如数组已满,且已超过指定的时间,则返回 false;如未超过指定的时间,则调用 notFull condition 的 awaitNanos 方法进行等待,如为被唤醒或超时,则继续判断数组是否已满;如线程被 interrupt,则直接抛出 InterruptedException。
另一个不带时间的 offer 方法在数组满的情况下则不进入等待,而是直接返回 false,如果有需求,也可以选择调用 put 方法,此方法在数组已满的情况下会一直等待,直到数组不为空或线程被 interrupt。
三、poll(E,long,TimeUnit)
此方法用于获取队列中的第一个元素,如队列中没有元素,则进入等待,与 offer(E,long,TimeUnit) 相同,它也是在三种情况后继续,来看看具体实现。
将指定的时间转化为纳秒,并加锁,如数组中的元素个数不为零,则从当前的对象数组中获取最后一个元素,在获取后将该位置上的元素设置为 null;如数组中的元素个数为零,首先判断剩余的等待时间是否小于零,如小于则返回 null,如大于则调用 notEmpty condition 的 awaitNanos 方法进行等待,如为被唤醒或超时,则继续判断数组中元素个数是否不为零;如线程被 interrupt,则直接抛出 interruptedException。
另一个不带时间的 poll 方法在数组中元素个数为零的情况下则不进入等待,而是直接返回 null,如果有需求,也可以直接调用 take 方法,此方法在数组为空的情况下会一直等待,直到数组不为空或线程被 interrupt。
四、iterator()
在调用 iterator 方法时,首先进行加锁,然后创建一个 Itr 对象实例,Itr 对象实例在创建时首先获取到当前数组元素尾部的 index 以及尾部的元素,调用完毕后释放锁。
在调用 Itr 的 next 方法时,首先进行加锁,然后获取 Itr 中的 nextItem,增加需要获取的下一个元素 index,并检查下一元素是否存在,如存在则获取并赋值到 nextItem,如不存在则将 nextIndex 设置为 -1,nextItem 设置为 null。
根据以上分析可以看出,ArrayBlockingQueue 为一个基于固定大小数组、ReentrantLock 以及 Condition 实现的可阻塞的先进先出的 Queue。
除 ArrayBlockingQueue 之外,BlockingQueue 的实现上常用的还有 LinkedBlockingQueue,LinkedBlockingQueue 实现的不同为采用对象的 next 构成链表的方式来存储对象。由于读只操作对头,而写只操作队尾,这里巧妙地采用了两把锁,对于 put 和 offer 采用一把锁,对于 take 和 poll 则采用另外一把锁,避免了读写时互相竞争锁的现象,因此 LinkedBlockingQueue 在高并发读写操作都多的情况下,性能会较 ArrayBlockingQueue 好很多,在遍历以及删除元素则要两把锁都锁住。