简介
BlockingQueue定义了一个阻塞队列接口,JDK1.5引入,位于java.util.concurrent包中,由doug Lea开发,是juc包中阻塞队列的顶层接口。查看其UML类图关系:
BlockingQueue继承了Queue,那就满足了队列的基本特点FIFO,阻塞队列是线程安全的,因此被广泛的用于生产者消费者模型,此外当队列内容为空时,消费者端线程会进入阻塞状态,直到有数据进入队列,当队列容量达到最大值时,所有生产者线程会进入阻塞状态,直到队列有空余容量,线程会被自动唤醒。在阻塞队列没有指定容量大小的情况下,默认容量为Integer.MAX_VALUE。
BlockingQueue继承了Collection接口,因此会满足集合相关的部分操作如remove操作,但是并不一定会有效的执行。BlockingQueue是线程安全的,所有队列方法都使用内部锁或其他形式的并发控制以原子方式实现。但集合批量操作addAll、containAll、retainAll和removeAll不一定以原子方式执行,例如,addAll(c) 可能会在仅添加 c 中的某些元素后失败(抛出异常)。
BlockingQueue中的方法对于队列的操作可以包含两大类,新增和移除元素,对于每一类操作,都包含了四种形式的处理方法:
- 抛出异常:当无法成功新增或者移除元素时,抛出异常
- 返回特殊值:取决于操作,返回null 或 false
- 阻塞:无限期地阻塞当前线程,直到操作成功
- 阻塞超时:在给定的时间内阻塞,超时将放弃
下表总结了这些方法:
总结 | 抛出异常 | 返回值 | 阻塞 | 超时 |
---|---|---|---|---|
新增 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
移除 | remove() | poll() | take() | poll(time, unit) |
抽查 | element() | peek() | - | - |
由于抽查属于BlockingQueue继承自Queue中的方法,因此不提供阻塞功能,也无需阻塞,并且阻塞队列中不允许添加null元素。
接下来对阻塞队列中关于新增和移除两大类操作的基本方法进行介绍。
基本操作
add
如果可以在不违反容量限制的情况下立即将指定元素插入此队列,则在成功时返回 true,如果当前没有可用空间则抛出 IllegalStateException。当使用容量受限的队列时,通常最好使用 offer。
如果此时由于容量限制无法添加元素,会抛出IllegalStateException异常。
如果队列中的元素类型与指定集合的元素类型无法转换,会抛出ClassCastException。
如果指定元素的某些属性阻止它被添加到该队列中中,则会抛出IllegalArgumentException异常。
offer
如果可以在不违反容量限制的情况下立即将指定元素插入此队列,则在成功时返回 true,如果当前没有可用空间则返回 false。在使用容量受限的队列时,这种方法一般比add更可取,只有抛出异常才能插入元素失败。
如果有必要,可以使用参数offer(E e, long timeout, TimeUnit unit)指定offer超时等待的时间,在等待期间如果线程被打断则会抛出InterruptedException异常。
put
将指定元素插入此队列,如果队列空间不足,使用put方法会一直阻塞直到有可用空间。
take
take方法会取走队列头部元素并返回,如果队列为空会一直阻塞等待直到队列拥有元素,等待期间线程被打断会抛出InterruptedException异常。
poll
该方法同take会阻塞等待队列拥有元素,但是poll方法可以指定等待超时时间。
remove
remove方法继承自Collection接口,如果需要移除的元素o存在于队列中一个或者多个,从此队列中移除指定元素的单个实例,或者说如果此队列因调用而更改,则返回 true。
如果需要移除的元素o为null,则会抛出NullPointerException异常,如果需要移除的元素o与队列包含的元素类型不兼容会抛出ClassCastException异常。
drainTo
drainTo(Collection<? super E> c)方法提供了一个操作,将阻塞队列中所有可用元素添加到给定集合中并删除原队列中的元素,并返回所操作元素的个数,这样操作可能比重复轮询队列更有效,不需要考虑锁。
尝试将元素添加到集合c时遇到的失败可能会导致在引发相关异常时元素既不在集合中,又不属于任何一个集合或两个集合。
如果指定的集合不支持添加元素会抛出异常UnsupportedOperationException 。
如果队列中的元素类型与指定集合的元素类型无法转换,会抛出ClassCastException。
如果指定集合为空,则会抛出NullPointerException异常。
如果指定的集合包含的元素是这个队列、或者这个队列的某个元素的某些属性阻止它被添加到指定的集合中,则会抛出IllegalArgumentException异常。
其他方法
remainingCapacity
返回阻塞队列理想情况下(在没有内存或资源限制的情况下)可以不阻塞地接受的附加元素的数量,如果没有内在限制,则返回 Integer.MAX_VALUE。需要注意的是,不能总是通过检查剩余容量来判断插入元素的尝试是否会成功,在并发场景下可能存在另一个线程即将插入或删除元素的情况。
contains
此方法覆写自Collection接口,如果此队列包含指定元素,则返回true。更正式地说,当且仅当此队列包含至少一个元素e使得o.equals(e)时才返回true。
总结
Java并发变成包阻塞队列的引入,使得在使用多线程实现生产者、消费者模型时无需再关心线程的阻塞以及唤醒等操作,大大降低了开发的难度。而BlockingQueue本身也是扩充了Queue和Collection接口的特性,进行增强实现了队列阻塞的特性。
这里只是对阻塞BlockingQueue中定义的方法进行简单熟悉,具体的实现在后续各种实现类中再进行深入分析。