文章目录
前言
很多并发需求需要阻塞队列去实现,比如秒杀,将请求缓存到队列里面然后mq消费等等。Today we will take about ArrayBlockingQueue.
ArrayBlockingQueue
从字面意思:阻塞队列,在队列为空和满的时候都会阻塞
源码遨游
具体方法
我们可以看到大概方法有offer,put,take,poll等等
阻塞重要变量
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
lock为锁,控制并发的。
notEmpty,当队列为空的时候阻塞对象
notFull,当队列满的时候阻塞对象
课外充电:Condition则为lock生成Condition,用来代替Object(当Object调用wait等等方法时,需要获得资源的监视器,而Condition不用,但是需要搭配ReentranLock一起使用)
take
获得value方法
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
流程:先锁公共的锁,跟内部并发有关系。如果队列为空,一直阻塞notEmpty Condition,直到队列有值。
至于上面为啥使用lockInterruptibly,而不是lock,请看下节分解~
lockInterruptibly
take是无线循环地等待状态,如果该线程被打断的时候,它会唤醒该线程进行处理InterruptedException异常。我们可以打断线程,不让它一直等待。
poll
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
可以看到这里是不会等待或者阻塞的。
poll(long timeout, TimeUnit 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) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
lock.unlock();
}
}
notEmpty.awaitNanos(nanos);带时间,多长时间获取值。
获取方法小结
take方法会阻塞,offer有带时间的参数。
如果说需求没有要阻塞的话,尽量使用poll带参数的方法,不然线程一直在那里等待。
不过有个场景还是挺适合的,就是把请求扔到队列中,我们可以阻塞到它有值进行接下来的操作,而不用一直去定时查看。这个也是BlockingQueue本来的想法,不是吗?
阻塞到有值 |
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();
}
}
这里使用notFull进行阻塞,等待。直到队列数量不等于最大数量咯~
offer
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();
}
}
这是一个带有时间的设置值的方法。