阻塞队列:
- 队列,先进先出的一个数据结构
- 阻塞,基于ReentrantLock实现的,并且线程的挂起也是通过Condition
ArrayBlockingQueue底层是采用数组实现的一个队列。因为使用的是数组,又被称为有界队列。
1 ArrayBlockingQueue应用
存数据操作 add(E),offer(E),put(E),offer(E,time,unit)
- add(E):添加数据到队列,如果满了,抛异常。
- offer(E):添加数据到队列,如果满了,返回false
- put(E):添加数据到队列,如果满了,线程挂起
- offer(E,time,unit):添加数据到队列,如果满了,线程挂起一段时间
取数据操作 remove(),poll(),take(),poll(time,unit)
- remove():从队列拿数据,拿到返回,拿到null,抛异常
- poll():从队列拿数据,拿到返回,拿到null,也返回
- take():从队列拿数据,拿到返回,没数据,一直阻塞
- poll(time,unit):从队列拿数据,拿到返回,没数据,阻塞time时间
2 ArrayBlockingQueue存取源码分析
2.1 存数据
- enqueue,数据存入数组的方法,供其他方法调用
// 存放数据到数组中
private void enqueue(E x) {
// 拿到数组
final Object[] items = this.items;
// 数组放进去
items[putIndex] = x;
// 把put指针++, 指针是否已经到了最后一个位置,归位到0位置。
if (++putIndex == items.length)
// 归位到0位置。
putIndex = 0;
// 数据条数 + 1
count++;
// 唤醒在阻塞的取数据线程
notEmpty.signal();
}
- offer,添加时,先判断队列满了没,满了就返回false
// 存数据
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();
}
}
- offer(time,unit),添加时,先判断队列满了没,满了先阻塞time时间,自动唤醒,还是满的,也返回false
// 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();
}
}
- put,添加时,先判断队列满了没,满了就阻塞,阻塞到被唤醒,或者被中断
// 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();
}
}
2.2 取数据
- dequeue,从数组中拿出数据的实际方法
private E dequeue() {
final Object[] items = this.items;
// 取数据
E x = (E) items[takeIndex];
// 将取完的位置置位null
items[takeIndex] = null;
// take指针++,如果到头,归位0~~
if (++takeIndex == items.length)
takeIndex = 0;
// 数据条数 - 1
count--;
// 唤醒队列满的时候,阻塞住的写线程
notFull.signal();
return x;
}
- poll,取数据,队列为空时返回null
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// count == 0代表没数据, 就返回null,有数据走dequeue
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
- poll(long timeout, TimeUnit unit),取数据,队列为空时阻塞,超时后返回null
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();
}
}
- take,取数据,队列为空时阻塞,直到队列中有数据了,取出并返回
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await(); // 挂起线程,需要被唤醒
return dequeue();
} finally {
lock.unlock();
}
}