前言
本篇文章会介绍BlockingQueue的概念。
BlockingQueue
是java线程池的核心。无论是在面试还是工作中,都是必知必会的知识点。
BlockingQueue的名字就完美概括了这个接口的特点,Queue
是BlockingQueue的父接口,说明它本质还是一个队列。Blocking
意为阻塞,说明BlockingQueue相对于普通队列,新增了阻塞的特性。
本文会从BlockingQueue的父接口Queue
开始聊起,包括Queue在java 容器中的定位以及API等等。然后介绍BlockingQueue相比于Queue新增出来的方法。最后会介绍几个BlockingQueue接口的具体实现类的特点和具体用法。
从父接口Queue开始聊起
Queue接口是Java容器大家庭中的一员,具体的定位如下图所示:
Queue自从java 1.5之后,成为接口Collection
下的一员,Queue与List
和Set
最大的区别就在于,Queue是专门为高并发存在的。
除去基本的Collection自带的方法,Queue额外提供了增、删、查的方法。每一种操作又会有两种具体的实现方法,区别在于操作失败时,是抛出错误还是返回某个特定值。
下面对这些方法做一个总结
抛出异常 | 返回特定值 | |
---|---|---|
增 | add() | offer() – 返回false |
删 | remove() | poll() – 返回null |
查 | element() | peek() – 返回null |
注意,其中的查操作和删操作,只能操作队列的头节点,并不能对队列中间的节点执行操作。
BlockingQueue接口及具体实现类
BlockingQueue接口API
由于BlockingQueue是Queue的子接口,所以上面的方法都包含在BlockingQueue中。
而BlockingQueue与Queue的主要区别,在于它新增了阻塞方法 。
什么是阻塞方法呢?简而言之,是当这个操作暂时没法完成的时候,会阻塞住当前线程,直到这个操作完成或者超时。
比如向队列中新增一个元素,如果队列已经满了,这个新增操作就会阻塞住线程,接下来根据选择的具体方法的不同,要么会一直等到队列中有了空位,要么会等超时后返回某个特定值。
BlockingQueue,相对于Queue,新增了两种增加元素和删除元素的阻塞方法,而这些阻塞方法也分成两种类型,一种是一直等待,一种是超时返回特定值。
抛出异常 | 返回特定值 | 阻塞等待 | 超时返回 | |
---|---|---|---|---|
增 | add() | offer() – 返回false | put() | offer(e, time, unit) – 返回false |
删 | remove() | poll() – 返回null | take() | poll(time, unit) – 返回null |
查 | element() | peek() – 返回null | 不存在 | 不存在 |
表中没有标红的方法是继承自Queue接口中的方法,而标红的则是BlockingQueue新增的方法。
具体实现类
BlockingQueue本身也只是个接口,真正发挥作用的是它的具体实现类。
而它的实现类还可以进一步的分类:
如果队列中的元素是按照先进先出排序的,那么可以用LinkedBlockingQueue
和ArrayBlockingQueue
。
如果要自己实现排序原则,就需要PriorityBlockingQueue
和DelayQueue
。
如果要实现添加元素的线程继续追踪该元素直到该元素被取出,就需要SynchronousQueue
和TransferQueue
。
LinkedBlockingQueue 和 ArrayBlockingQueue
这两种具体实现的区别就在于底层的数据结构,一个使用链表而另一个使用数组。
从而也就导致了容量上的不同,理论上LinkedBlockingQueue
的容量理论上是无限的,而ArrayBlockingQueue
的容量受底层的数组限制。
从两者的构造函数就能看出上述区别,ArrayBlockingQueue的构造函数要求必须声明容量上限,而LinkedBlockingQueue可以不声明上限,默认是Integer.MAX_VALUE的值。
PriorityBlockingQueue 和 DelayQueue
Queue本身是先进先出的队列,各个元素在队列里的顺序是按照加入到容器的先后顺序。
但是PriorityBlockingQueue和DelayQueue就可以自定义元素在队列中的排序原则。
PriorityBlockingQueue默认使用元素的compareTo
方法,按从小到大的顺序排列元素。调用take()
会获取到队列中最小的元素。
也可以在调用构造函数时声明自定义的Comparator
,队列中的元素会按照Comparator指定的规则进行排序。
DelayQueue要求队列中的所有的元素都实现Delayed接口,这些元素必须实现的方法是getDelay(TimeUnit unit)
和 compareTo()
方法。
元素在队列中按照compareTo指定的顺序排列。但是,将队列元素取出的操作还依赖于这个元素是否已经过期。这里过期的含义是getDelay()方法返回0或者负数。
比如下面这段代码:
public class BlockingQueueDemo {
public static void main(