ArrayBlockingQueue构造:
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
//初始化队列,并初始化锁和Condition
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();
}
//初始化队列,并给队列添加指定集合内的元素。
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
lock.lock(); // 锁定只用于可见性,而不是互斥
try {
int i = 0;
try {
for (E e : c) {
checkNotNull(e);
items[i++] = e;
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
count = i;
putIndex = (i == capacity) ? 0 : i;
} finally {
lock.unlock();
}
}
//队列中定义的锁变量和Condition
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
从源码中可以看出,初始化队列容量时,会同时创建锁对象和两个Condition对象,用于实现阻塞队列实现和线程安全控制。
添加元素方法:add、offer、put
add、offer:
public boolean add(E e) {
return super.add(e);
}
supper.add(e)调用的是AbstractQueue中的add方法:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
这里的add方法又调用了offer方法,而这里的offer方法又是在ArrayBlockingQueue中实现的:
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length) //如果队列已满,返回false
return false;
else {
insert(e);
return true;
}
} finally {
lock.unlock();
}
}
着这里可以看出add方法和offer方法本质上是一样,唯一的区别是当offer失败时,add方法会抛IllegalStateException
put:
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); // 如果当前线程未被中断,则获取锁
try {
while (count == items.length) //队列已满,则阻塞当前线程
notFull.await();
insert(e);
} finally {
lock.unlock();
}
}
offer和put方法中都有checkNotNull方法:
private static void checkNotNull(Object v) {
if (v == null)
throw new NullPointerException();
}
可以看到如果添加的元素为空,会抛出一个NullPointerException,从而保证了队列中元素非空。
另外put和offer向队列中添加元素,实际调用的是insert方法:
private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
notEmpty.signal();
}
每次添加元素的时候,都会调用notEmpty.signal()来唤醒notEmpty线程。
移除元素remove、poll、take:
remove、poll:
ArrayBlockingQueue中没有remove()方法,实际使用的事AbstractQueue中的remove()方法:
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
可以看到与add方法类似,这里实际调用的事ArrayBlockingQueue中的poll方法:
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : extract();
} finally {
lock.unlock();
}
}
take:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return extract();
} finally {
lock.unlock();
}
}
take和pool中都时通过extract()方法移除并获取元素:
private E extract() {
final Object[] items = this.items;
E x = this.<E>cast(items[takeIndex]);
items[takeIndex] = null;
takeIndex = inc(takeIndex);
--count;
notFull.signal();
return x;
}
extract()方法中获取元素后,先调用notFull.signal()方法唤醒notFull线程,然后再返回获取的元素。
检查元素element、peek:
element()方法实际调用的是AbstractQueue中的方法:
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
peek()方法:
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : itemAt(takeIndex);
} finally {
lock.unlock();
}
}
final E itemAt(int i) {
return this.<E>cast(items[i]);
}
小结:
从上面的源码中可以发现,阻塞队列的添加、移除、检查的几个方法,对于同一类操作,本质上是相同的。
返回特殊值和抛异常,其实只是对其中一个特殊值手动抛出异常;返回特殊值与阻塞,只是利用Condition的await和signal进行线程的阻塞和唤醒。