ArrayBlockingQueue是基于数组实现的有序队列。
ArrayBlockingQueue实现元素添加有下面三个方法:
public boolean add(E e)
public boolean offer(E e)
public void put(E e) throws InterruptedException
获取元素也有下面思个方法:
public E remove()
public E poll()
public E take() throws InterruptedException
public E peek()
那么这些方法有什么区别呢?
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable {
/**
* Serialization ID. This class relies on default serialization
* even for the items array, which is default-serialized, even if
* it is empty. Otherwise it could not be declared final, which is
* necessary here.
*/
private static final long serialVersionUID = -817911632652898426L;
/** The queued items */
final Object[] items;
/** items index for next take, poll, peek or remove */
int takeIndex;
/** items index for next put, offer, or add */
int putIndex;
上面的代码中我们可以看到Object[] 用来存放元素,是基于数组结构。
takeIndex 用来存放下一个可以获取的元素的下标。
putIndex 用来存放下一个可以添加的元素的下标。
一:offer方法:
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
private static void checkNotNull(Object v) {
if (v == null)
throw new NullPointerException();
}
offer方法很简单
1:首先检查元素是否非空,如果添加的元素为null,则抛出NullPointerException
2:判断元素队列中的个数是否等于数组长度,如果是 ,则返回false。
3:前面两步不满足,则进行入队操作。
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;
count++;
notEmpty.signal();
}
入队方法也很简单,直接将元素置入数组相应位置。然后对putIndex进行++
如果putIndex的值等于数组长度则置0,因为putIndex和takeIndex是循环利用的。
最后一行通知Condition notEmpty信号量。
从上面我们可以看到offer和普通的队列入队操作,并无其他不同,看不出Blocking的作用。
二:add方法:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
add方法调用offer方法,如果调用成功,则返回true,否则抛出IllegalStateException("Queue full")异常。
三:put方法:
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();
}
}
put添加元素时,如果队列已满,此时会调用notFull.await,阻塞线程。直到有元素出队列,dequeue()方法执行,调用notFull.signal();
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}
从上面的源码我们可以看到三个添加方法的区别:
offer 添加元素入队列,如果队列满,则返回false
add 添加元素入队列,如果队列满,则抛出异常
put 添加元素入队列,如果队列满,则阻塞当前线程,直到队列有其他元素出队列,执行入队。
接下去我们看出队方法:
一:poll方法
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
poll方法和offer方法有点类型,如果队列为空,则返回null,否则执行元素出队操作。
二:remove方法
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
remove调用poll执行元素出队,如果队列为空,则抛出NoSuchElementException异常。
三:peek方法
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // null when queue is empty
} finally {
lock.unlock();
}
}
peek方法并没有执行元素出队方法,只是返回当前takeindex元素的值。
四:take方法
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
当队列个数为0时,take方法会阻塞。直到enqueue方法执行入队操作,调用 notEmpty.signal();
上面我们可以看到四个取元素的区别
poll 队列为空,则返回false 否则执行出队操作
remove 队列为空,则抛出异常 否则执行出队操作
peek 只是返回队列头元素,并不会执行出队操作
take 队列为空,则线程阻塞,直到有元素入队,执行出队操作。