引言
这篇文章,我们来看jdk中阻塞队列的类体系结构中几个重要的类和接口。
阻塞队列的类型
名称 | 描述 |
---|---|
ArrayBlockingQueue | 一个由数组结构组成的有界阻塞队列 |
LinkedBlockingQueue | 一个由链表结构组成的有界阻塞队列 |
PriorityBlockingQueue | 一个支持优先级排序的无界阻塞队列 |
DelayQueue | 一个使用优先级队列实现的无界阻塞队列 |
SynchronousQueue | 一个不存储元素的阻塞队列 |
LinkedTransferQueue | 一个使用链表结构组成的无界阻塞队列 |
LinkedBlockingDeque | 一个由链表结构组成的双向阻塞队列 |
ArrayBlockingQueue的类图
Queue接口的重要方法
我们先从跟队列相关的第一个接口Queue看起:
public interface Queue<E> extends Collection<E> {}
它是Collection的子接口,Collection是jdk中很多集合如list、set的公共接口,它给出了一系列集合的公共方法,如add(E)、remove(Object)、size等。由于我们这个系列的重点是阻塞队列,所以不去描述它的这些方法。
Queue代表队列,它是一种先进先出的数据结构,提供了以下方法:
boolean add(E e);
E remove();
boolean offer(E e);
E poll();
E element();
E peek();
这六个方法从不同的纬度可以进行不同的划分。总体描述一下就是,add和offer方法都用来向队列中添加元素(队尾),remove和poll都用来删除队列中的元素(队列头),element和peek都用来获取队列中的元素(队列头)但是不删除该元素。add和remove是相互对应的添加和删除方法,当添加时队列空间不足或者删除时队列为空会抛出异常,而offer和poll分别能实现add与remove的功能但出现上述情况时不会抛出异常,offer在队列已满时会返回false,poll在队列为空时返回null。element和peek方法的区别也在于是否抛出异常,当队列为空时,element方法会抛出NoSuchElementException异常而peek只是返回null。
下表对这六个方法进行了简单的对比:
方法 | 功能 | 是否抛出异常 | 异常类型 |
---|---|---|---|
add | 向队尾添加元素 | 在队列已满时抛出 | IllegalStateException |
offer | 向队尾添加元素 | 否 | |
remove | 删除队首元素 | 在队列为空时抛出 | NoSuchElementException |
poll | 删除队首元素 | 否 | |
element | 获取但不删除队首元素 | 在队列为空时抛出 | NoSuchElementException |
peek | 获取但不删除队首元素 | 否 |
理解了Queue接口的六个方法之后,我们来看AbstractQueue。AbstractQueue类实现了Queue接口并给出了部分方法的默认实现。
AbstractQueue对队列方法的实现
AbstractQueue的定义如下:
public abstract class AbstractQueue<E>
extends AbstractCollection<E>
implements Queue<E> {}
首先大致描述一下,AbstractQueue只给出了add、remove和element方法的实现,也就是三个会抛出异常的方法。并且这三个方法的实现都是通过调用与之对应的不会抛出异常的方法来完成的。add方法调用了offer方法,remove方法调用了poll方法,element方法调用了peek方法,但是offer、poll和peek方法需要子类自己实现逻辑:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
代码很容易理解,就是在对应没有异常抛出的情况下做返回值的判断,如果为false或者null,就抛出对应的异常。
阻塞队列BlockingQueue接口
看完了队列的基本方法,我们来看阻塞队列。阻塞队列的最上层接口是BlockingQueue。它的定义如下:
public interface BlockingQueue<E> extends Queue<E> {}
既然是阻塞队列,那么BlockingQueue必然要增加阻塞相关的方法。在Queue的基础上,BlockingQueue接口增加了以下四个方法:
void put(E e) throws InterruptedException;
E take() throws InterruptedException;
boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException;
E poll(long timeout, TimeUnit unit)
throws InterruptedException;
这四个方法同样是成对存在的。put和take分别是阻塞地添加和删除元素,如果添加时队列已满或者删除时队列为空,线程就会阻塞直到能进行相应操作。带有时间限制的offer和poll方法也是阻塞地来执行添加和删除操作,不过它们阻塞的时间通过参数进行了限制,也就是会超时退出。再加上Queue接口中的add/remove和没有时间参数的offer/poll方法(这四个方法非阻塞),BlockingQueue中能进行队列添加删除操作的方法就有八个了。
小结
这篇文章,我们大致分析了阻塞队列的类层次结构,梳理了在类体系结构中几个重要的接口和类如Queue、AbstractQueue、BlockingQueue的含义以及关键的方法。到目前为止,我们还没有看到对队列进行添加和删除操作的方法的具体逻辑,下一篇文章,我们通过ArrayBlockingQueue为例来分析阻塞队列的具体实现。