阻塞队列类常用语多线程的生产者-消费者模式,作为生产者和消费者的消息中间件使用,作为中间件就必须支持阻塞等待作用,在ArrayBlockingQueue 中使用的
阻塞等待工具类是ReentrantLock 类,这个类的功能分析在博主的其他博客中已经通过源码分析的形式给出,请读者参考理解。
首先我们看的是ArrayBlockingQueue类的主要属性和方法:
属性:
** The queued items */
final Object[] items; //Object数组存放中间值,生产者将数据放入items中,消费者从items中获取
int takeIndex;// 这是一个索引值,指的是items中消费者要获取中间值的索引。该索引对应的值存在,消费者获取索引对应的值后,索引往后移动。
/** items index for next put, offer, or add */
int putIndex; // 这个也是个索引值,指的是生产者要放中间值的索引,放入值后,索引值往后移动,指向下一次生产者放入值的位置索引。索引指向
//的空间值不是中间消息值。
对应这两个索引和对象数组一起 决定了生产者-消费者之间的值存放逻辑,如下图所示:
方法:
/**
* Inserts the specified element at the tail of this queue, waiting
* for space to become available if the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
//这个方法会在items存放中间结果值都满了的时候会阻塞等待
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)//items空间已经满了
notFull.await();//阻塞等待
insert(e);
} finally {
lock.unlock();
}
}
put方法就用来作为阻塞插入中间结果,实现了类的自动阻塞。
当然JDK的强大之处就是在于除了满足开发者需求外,还会提供更多的选择,函数 public boolean offer(E e, long timeout, TimeUnit unit),这个同put函数一样有阻塞的功能
而且这个还支持设置等待时间,过了等待时间线程还没有获得资源就会插入失败,返回false。
/**
* Inserts the specified element at the tail of this queue, waiting
* up to the specified wait time for space to become available if
* the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
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) { //items满了
if (nanos <= 0) //如果nanos为0或负数就终止插入元素e,返回false;
return false;
nanos = notFull.awaitNanos(nanos); //如果等待时间过后还items还是满的状态,就返回值0或负数
}
insert(e); //插入中间值操作
return true;
} finally {
lock.unlock();
}
}
offer函数功能是在解决put函数的不足:如果items一直处于满的状态或是一直被其他生产者线程竞争下来,那么put就会一直处于等待状态;但是offer函数会在给出的等待
时间过后自动返回推出,当然中间值的插入也就失败了。
与put函数对应生产者获取中间值的函数:take方法。这个方法会在items中间值数量为0的时候一直等待下去。
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)//判断出中间值队列为空
notEmpty.await();//等待
return extract();//执行获取中间值;
} finally {
lock.unlock();
}
}
//为了解决take函数一直阻塞等待的问题,poll函数就这样被JDK大神放出来了。
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) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return extract();
} finally {
lock.unlock();
}
}
这里就对这个方法不做赘述了。
当然ArrayBlockingQueue类的API还有很多其他方法,其他 的方法就不想本文中提到的4个函数一样阻塞等待了。各位如果有兴趣可以研究下,并自己写个demo
小结: 这个类中用到的数据结构就是简单的队列,IFOF的规则,提供了存值和取值的操作,只是起到了消息队列中间件的结果,比较简单。