ArrayBlockingQueue,顾名思义:基于数组的阻塞队列。数组是要指定长度的,所以使用ArrayBlockingQueue时必须指定长度,也就是它是一个有界队列。

它实现了BlockingQueue接口,有着队列、集合以及阻塞队列的所有方法,队列类图如下图所示(图片来自之前的文章:说说队列Queue

【java多线程】队列系统之ArrayBlockingQueue源码

既然它在JUC包内,说明使用它是线程安全的,它内部使用ReentrantLock来保证线程安全。ArrayBlockingQueue支持对生产者线程和消费者线程进行公平的调度,默认情况下是不保证公平性的。公平性通常会降低吞吐量,但是减少了可变性和避免了线程饥饿问题。

2、数据结构

通常,队列的实现方式有数组和链表两种方式。对于数组这种实现方式来说,我们可以通过维护一个队尾指针,使得在入队的时候可以在O(1)的时间内完成;但是对于出队操作,在删除队头元素之后,必须将数组中的所有元素都往前移动一个位置,这个操作的复杂度达到了O(n),效果并不是很好。如下图所示:

 【java多线程】队列系统之ArrayBlockingQueue源码

为了解决这个问题,我们可以使用另外一种逻辑结构来处理数组中各个位置之间的关系。假设现在我们有一个数组A[1…n],我们可以把它想象成一个环型结构,即A[n]之后是A[1],相信了解过一致性Hash算法的童鞋应该很容易能够理解。如下图所示:我们可以使用两个指针,分别维护队头和队尾两个位置,使入队和出队操作都可以在O(1)的时间内完成。当然,这个环形结构只是逻辑上的结构,实际的物理结构还是一个普通的数据。【java多线程】队列系统之ArrayBlockingQueue源码

讲完ArrayBlockingQueue的数据结构,接下来我们从源码层面看看它是如何实现阻塞的。

3、源码分析

3.1、属性

//队列的底层结构
final Object[] items;

//队头指针
int takeIndex;
//队尾指针
int putIndex;

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

final ReentrantLock lock;

//并发时的两种状态
private final Condition notEmpty;
private final Condition notFull;
View Code

相关文章: