ArrayBlockingQueue
基于数组的阻塞队列,底层数据结构为定长的数组,实例化的时候必须指定数组的长度
1 核心参数
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/** 存储数据的数组 */
final Object[] items;
/** 队头指针,当前可以获得元素的下标*/
int takeIndex;
/** 队尾指针,当前可以存储元素的下标
* 当队列满时putIndex=0
*/
int putIndex;
/** 当前队列中的数据总数 */
int count;
/** 全局可重入锁,用来控制队列中元素的操作 */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
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;
................
}
2 构造方法
/**
* 创建一个指定长度的队列, 非公平锁
* @param capacity 指定的队列长度
*/
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
/**
* 创建指定长度和指定是否公平的队列
* @param capacity 指定的队列长度
* @param fair 是否公平: true: 遵循现在先出的原则, false
* @throws IllegalArgumentException if {@code capacity < 1}
*/
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();
}
/**
* 根据给定的长度、是否公平和集合数组创建队列
* 当集合长度大于capacity 则抛出异常
* @param capacity 指定的队列长度
* @param fair 是否公平: true: 遵循现在先出的原则, false
* @param c 给定的集合长度
*/
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
.......
}
3 核心方法
添加数据基本流程如下
-
add(E e)
添加指定数据到队列,成功返回true, 失败抛出IllegalStateException异常,add底层调用的是offer
// 底层调用的是 offer()
public boolean add(E e) {
return super.add(e);
}
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
-
offer(E e)
添加指定数据到队列,成功返回true, 失败返回 false
public boolean offer(E e) {
checkNotNull(e);
// 获得锁
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
// 如果队列满了就直接返回false
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;
// 总数+1
count++;
// 添加元素后,同时通知唤醒在等待的消费者
notEmpty.signal();
}
-
offer(E e, long timeout, TimeUnit unit)
指定等待时间添加数据,该方法和offer(E e)方法基本一致,但是也有以下的不同之处
- 添加的锁是可中断锁
- 在等待时间内可以被中断
/**
* @param e 添加的元素
* @param timeout 等待时间
* @param unit 等待时间单位
*/
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
// 计算需要等待的毫秒值
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
// 添加可中断锁
lock.lockInterruptibly();
try {
while (count == items.length) {
// 循环等待,直到满足等待时间
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
获取数据的基本流程如下图
-
put(E e)
添加指定数据到队列,如果队列满了,则阻塞当前线程,直到抛出InterruptedException异常
public void put(E e) throws InterruptedException {
// 判空
checkNotNull(e);
// 获得锁
final ReentrantLock lock = this.lock;
// 添加可中断锁
lock.lockInterruptibly();
try {
while (count == items.length)
// 如果队列满了,就一直阻塞等待
// 直到被通知唤醒
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
-
poll()
取出队列的头部的元素,获取不到返回null
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;
// 获得数据,并把当前位置设置为null
E x = (E) items[takeIndex];
items[takeIndex] = null;
// +1后的队尾指针如果等于数组长度,说明数组已经到尾部了
// 需要从头再开始存放数据
if (++takeIndex == items.length)
takeIndex = 0;
// 总数-1
count--;
if (itrs != null)
itrs.elementDequeued();
// 非满条件通知唤醒生产者可以生产数据了
notFull.signal();
return x;
}
-
poll(long timeout, TimeUnit unit)
取出队列的头部的元素,如果没有元素则等待指定时间直到成功,获取不到返回null
等到过程中断线程则抛出InterruptedExcepution
/**
* @param timeout 等待时间
* @param unit 等待时间单位
*/
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
// 计算等待毫秒值
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
// 添加可中断锁
lock.lockInterruptibly();
try {
while (count == 0) {
// 超过等待时长,直接返回null
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
// 释放锁
lock.unlock();
}
}
-
take()
取出队列的头部的元素,如果没有元素则一直等待直到成功
等到过程中断线程则抛出InterruptedExcepution
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
// 可中断锁
lock.lockInterruptibly();
try {
// 如果为null, 则一直等待,直到被生产线程唤醒
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
// 释放锁
lock.unlock();
}
}