前面一文介绍了有界链表阻塞队列LinkedBlockingQueue,今天来说一说有界数组阻塞队列ArrayBlockingQueue。
ArrayBlockingQueue类中的常用方法和前文介绍的LinkedBlockingQueue类常用方法一样,内部源码实现也大同小异。ArrayBlockingQueue类初始化一个固定大小的数组items,使用ReentrantLock保证数据的原子性,相比于LinkedBlockingQueue有两个ReentrantLock对象,ArrayBlockingQueue类中只有一个ReentrantLock对象,读写操作都需要获取同一个ReentrantLock对象。另外,还有两个条件变量,条件变量内部都有一个条件队列用来存放进队和出队阻塞的线程,其实就是生产者-消费者模型。这些主要的成员变量如下:
final Object[]items;
final ReentrantLocklock;
private final ConditionnotEmpty;
private final ConditionnotFull;
offer(E e)
向队里尾部插入一个元素,如果队列有空闲则插入成功后返回true,如果队列已满则丢弃当前元素并返回false。
public boolean offer(E e) {
//(1)检查e是否为null,是则抛出NullPointerException
checkNotNull(e);
//(2)尝试获取lock对象。
final ReentrantLock lock = this.lock;
lock.lock();
try {
//(3)如果队列已满则丢弃当前元素并返回false
if (count == items.length)
return false;
else {
//(4)队列未满,则在队尾添加元素,并调用notEmpty.signal(),通知阻塞的消费者可以进行消费数据。
enqueue(e);
return true;
}
} finally {
//(5)释放锁。
lock.unlock();
}
}
put(E e)
向队列队尾插入一个元素,如果队列中有空闲则插入后直接返回,如果队列已满则阻塞当前线程,直到队列中有空闲插入成功后返回。线程阻塞时如果被其他线程设置了中断标志,则该线程会抛出InterruptedException异常。
public void put(E e) throws InterruptedException {
//(1)检查e是否为null,是则抛出NullPointerException
checkNotNull(e);
//(2)尝试获取锁对象,调用的是lockInterruptibly方法,所以在当其他线程设置了中断标志,该线程会抛出InterruptedException异常。
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//(3)队列已满则阻塞当前线程
while (count == items.length)
notFull.await();
//(4)队列未满,则在队尾添加元素,并调用notEmpty.signal(),通知阻塞的消费者可以进行消费数据。
enqueue(e);
} finally {
//(5)释放锁。
lock.unlock();
}
}
poll()
从队列头部获取一个元素,并从队列里移除该元素,如果队列为空则返回null。
public E poll() {
//(1)尝试获取锁。
final ReentrantLock lock = this.lock;
lock.lock();
try {
//(2)队列不为空则获取头部元素并移除,调用notFull.signal()方法,通知阻塞的生产者可以生产数据。
return (count == 0) ? null : dequeue();
} finally {
//(3)释放锁。
lock.unlock();
}
}
peek()
获取队列头部的元素但不从队列中移除该元素,队列为空的时候返回null。
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // null when queue is empty
} finally {
lock.unlock();
}
}
take()
获取队列中头节点元素并从队列里移除该节点,如果队列为空则会阻塞当前线程直到队列中有元素。如果当前线程在阻塞时被其他线程设置了中断标志,则会抛出InterruptedException异常。
public E take() throws InterruptedException {
//(1)尝试获取锁对象,调用的是lockInterruptibly方法,所以在当其他线程设置了中断标志,该线程会抛出InterruptedException异常。
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//(2)队列为空则进行阻塞等待。
while (count == 0)
notEmpty.await();
//(3)队列不为空则获取头部元素并移除,调用notFull.signal()方法,通知阻塞的生产者可以生产数据。
return dequeue();
} finally {
//(4)释放锁。
lock.unlock();
}
}
size()
相比于LinkedBlockingQueue类中的size方法,本类中的size需要获取锁,因为本类中的count变量没有volatile变量修饰无法保证内存的可见性。
public int size() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
今天的分享就到这,有看不明白的地方一定是我写的不够清楚,所有欢迎提任何问题以及改善方法。