定义
阻塞队列(BlockingQueue) 是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。
java中的阻塞队列
阻塞队列种类 | 描述 |
---|---|
ArrayBlockingQueue | 一个由数组结构组成的有界阻塞队列 |
LinkedBlockingQueue | 一个由链表结构组成的有界阻塞队列 |
PriorityBlockingQueue | 一个支持优先级排序的无界阻塞队列 |
DelayQueue | 一个使用优先级队列实现的无界阻塞队列 |
SynchronousQueue | 一个不存储元素的阻塞队列 |
LinkedTransferQueue | 一个由链表结构组成的无界阻塞队列 |
LinkedBlockingDeque | 一个由链表结构组成的双向阻塞队列 |
在JDK提供的阻塞队列中,除了LinkedBlockingDeque 是一个 Deque(双向的队列),其实现的接口是 BlockingDeque;其余6个阻塞队列则是 Queue(单向队列),实现的接口是 BlockingQueue。
对于单向 BlockingQueue常用方法描述
上述方法中,remove、element、offer、poll、peek都是属于Queue接口,其中add、remove、element在操作队列的时候,如果没有达到目的会抛出异常。而offer、poll、peek则只是给出一个操作状态的提示。
而对于take和put两种方法则会在有阻塞时,等待队列达到操作要求,完成操作,而不是直接返回结果。
对于四种单向队列解释
-
LinkedBlockingQueue
LinkedBlockingQueue的容量在不指定的情况下为 Integer.MAX_VALUE,但是也可以指定其最大容量,其中主要用到 put 和 take 方法,它是基于链表的队列,此队列按 FIFO (先进先出)排序元素,是线程安全的。
-
ArrayBlockingQueue
ArrayBlockingQueue在构造时需要指定容量,并可以选择是否需要公平性,如果公平参数被设置为 true,等待时间最长的线程会优先得到处理(其实就是通过将 ReentrantLock 设置为 true 来达到这种公平性:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序
-
PriorityBlockingQueue
PriorityBlockingQueue是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对 PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞 队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元 素要具有比较能力。
-
DelayQueue
DelayQueue(基于PriorityQueue来实现的)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。
阻塞队列的实现原理
阻塞队列实际上是使用通知模式实现的,即当生产者在往满的队列里添加元素时会阻塞生产者,当消费者消费了一个队列中的元素的时候会通知生产者当前队列可用,可以继续生产了。