![](https://cdn.zimug.com/wx-zimug.png)
Java BlockingQueue接口`java.util.concurrent.BlockingQueue`表示一个可以存取元素,并且线程安全的队列。换句话说,当多线程同时从 Java`BlockingQueue`中插入元素、获取元素的时候,不会导致任何并发问题(元素被插入多次、处理多次等问题)。
从java `BlockingQueue`可以引申出一个概念:阻塞队列,是指队列本身可以阻塞线程向队列里面插入元素,或者阻塞线程从队列里面获取元素。比如:当一个线程尝试去从一个**空队列**里面获取元素的时候,这个线程将被阻塞直到队列内元素数量不再为空。当然,线程是否会被阻塞取决于你调用什么方法从`BlockingQueue`获取元素,有的方法会阻塞线程,有的方法会抛出异常等等,下文我们会详细介绍。
## 一、BlockingQueue 接口实现类
本文不会去介绍如何自己实现`BlockingQueue`接口,JUC已经为我们做好了相关的一些接口实现类。
`BlockingQueue`是一个java接口,当我们需要使用阻塞队列的时候,可以使用它的实现类。`java.util.concurrent`包里面有如下的一些实现类实现了`BlockingQueue`接口。
* ArrayBlockingQueue
* DelayQueue
* LinkedBlockingQueue
* LinkedBlockingDeque
* LinkedTransferQueue
* PriorityBlockingQueue
* SynchronousQueue
在本文以及后续的文章中,会依次为大家介绍这些实现类的作用及使用场景,期待您的关注。
## 二、BlockingQueue 应用场景介绍
`BlockingQueue`通常被应用在一个线程生产对象放入队列,与此同时另一个线程消费队列内的对象的场景下。下面的这张图说明了使用场景:
![](http://cdn.zimug.com/6d31ddc1db95f22c90b498bb50a9e58a)
生产者线程不断的生产新的对象,并将他们插入到`BlockingQueue`,直到队列中object的数量达到队列存储容量的上限。也就是说当队列中对象达到容量上限的时候,生产者线程将被阻塞,不能再向队列中插入新的对象。生产者线程将保持阻塞等待状态,直到消费者线程从队列中拿走Object,让队列有空余位置放入新的对象。
消费者线程不断的从`BlockingQueue`取出对象并将其进行处理。如果消费者线程尝试从一个空队列中获取一个对象,消费者线程将被阻塞处于等待状态,直到生产者向队列中放入一个新的对象。
所以BlockingQueue经常被用于生产消费的缓冲队列,如果你不想用分布式的或者中间件消息队列(redis、kafka)等(因为对于一个小功能会增加比较大的独立中间件运维成本),BlockingQueue可以能是一个备选的选项。
### 2.1.BlockingQueue 方法介绍
Java`BlockingQueue` 提供了四组不同的方法用于向队列中插入、移除、检查队列中包含某一元素对象。每一组方法在被调用之后的响应行为上有所不同,如下:
||抛出异常|返回特定值|阻塞后一直等待|阻塞后等待超时|
|---|---|---|---|---|
|插入对象|add(o)|offer(o)|put(o)|offer(o, timeout, timeunit)|
|移除对象|remove(o)|poll()|take()|poll(timeout, timeunit)|
|检查对象存在|element()|peek()|||
上面的方法的四种行为分别的含义是
1. **抛出异常**: 如果调用方法后不能立即响应结果(空队列或满队列),则抛出异常。
2. **返回特定值**: 如果调用方法后不能立即响应结果(空队列或满队列),则返回特定的值(通常是true/false),true表示方法执行成功,否则表示方法执行失败。
3. **阻塞后一直等待**: 如果调用方法后不能立即响应结果(空队列或满队列),该方法将被阻塞一直处于等待状态。
4. **阻塞后等待超时**: 如果调用方法后不能立即响应结果(空队列或满队列),该方法将在一定时间范围内被阻塞等待,也就是在超时时间范围内阻塞。当超出超时时间之后,方法线程将不再阻塞,而是返回一个特定的值(通常是true/false),true表示方法执行成功,否则表示方法执行失败。
另外,`BlockingQueue`队列不允许向其内部插入null,如果你向队列中插入null,将会引发`NullPointerException`异常。
一般的队列都是从队首放入对象,从队尾获取对象,`BlockingQueue`不仅支持从队首队尾操作数据对象,还支持从队列中其他任何位置操作数据。比如:你已经向队列中放入一个对象并等待处理,但是出于某些特殊原因希望将这个对象从队列中删除掉。你可以调用`remove(o)`方法来删除队列中的一个特定的o对象。当然我们的程序不能经常性的这样做,因为队列这种数据结构经常从中间位置操作数据的效率是极低的,所以除非必要不建议这样做。
#### add(o)
BlockingQueue`add()` 方法可以将o对象以参数的形式插入到队列里面,如果队列里面有剩余空间,将被立即插入;如果队列里面没有剩余空间,`add()`方法将跑出 IllegalStateException.
#### offer(o)
BlockingQueue`offer()` 方法可以将o对象以参数的形式插入到队列里面,如果队列里面有剩余空间,将被立即插入;如果队列里面没有剩余空间,`offer()`方法将返回特定的值`false`.
#### offer(o, long millis, TimeUnit timeUnit)
BlockingQueue`offer()` 方法有另外一个版本的实现,存在超时时间的设置参数。这个版本的`offer()`方法将o对象以参数的形式插入到队列里面,如果队列里面有剩余空间,将被立即插入;如果队列里面没有剩余空间,调用offer方法的线程在超时时间内将被阻塞处于等到状态,当阻塞时间大于超时时间之后,队列内如果仍然没有剩余空间放入新对象,`offer()`方法将返回`false`.
#### put(o)
BlockingQueue`put()` 方法可以将o对象以参数的形式插入到队列里面,如果队列里面有剩余空间,将被立即插入;如果队列里面没有剩余空间,调用put的方法的线程将被阻塞,直到BlockingQueue里面腾出新的空间可以放入对象为止。
#### take()
BlockingQueue`take()` 方法取出并移除队列中的第一个元素(对象),如果BlockingQueue队列中不包含任何的元素,调用`take()`方法的线程将被阻塞,直到有新的元素对象插入到队列中为止。
#### poll()
BlockingQueue`poll()` 方法取出并移除队列中的第一个元素(对象),如果BlockingQueue队列中不包含任何的元素,`poll()`方法将返回`null`.
#### poll(long timeMillis, TimeUnit timeUnit)
BlockingQueue`poll(long timeMillis, TimeUnit timeUnit)`方法同样存在一个超时时间限制的版本,正常情况下该方法取出并移除队列中的第一个元素(对象)。如果BlockingQueue队列中不包含任何的元素,在超时时间范围内,如果仍然没有新的对象放入队列,这个版本的`poll()`方法将被阻塞处于等待状态;当阻塞时间大于超时时间之后,`poll(long timeMillis, TimeUnit timeUnit)`返回`null`
#### remove(Object o)
BlockingQueue`remove(Object o)` 方法可以从队列中删除一个以参数形式给定的元素对象,`remove()`方法使用`o.equals(element)`将传入参数o与队列中的对象进行一一比对,从而判定要删除的对象是否在队列中存在,如果存在就从队列中删除并返回true,否则返回false。
需要注意的是:如果队列中有多个与传入参数equals相等的对象,只删除其中一个,不会将队列中所有匹配的对象都删除。
#### peek()
BlockingQueue`peek()` 方法将取出队列中的第一个元素对象,但是并不会将其从队列中删除。如果队列中目前没有任何的元素,也就是空队列,`peek()`方法将返回`null`.
#### element()
BlockingQueue`element()`方法将取出队列中的第一个元素对象,但是并不会将其从队列中删除。如果队列中目前没有任何的元素,也就是空队列,`element()`方法将抛出 NoSuchElementException.
#### contains(Object o)
BlockingQueue`contains(Object o)` 方法用来判断当前队列中是否存在某个对象,该对象与传入参数o相等(`Objects.equals(o, element)`被用来判定对象的相等性)。遍历队列中的所有元素,一旦在队列中发现匹配的元素对象,该方法将返回true;如果没有任何的元素匹配相等,该方法返回false。
#### drainTo(Collection dest)
`drainTo(Collection dest)`方法一次性的将队列中的所有元素取出到集合类Collection dest对象中保存。
#### drainTo(Collection dest, int maxElements)
`drainTo(Collection dest)`方法一次性的从队列中取出maxElements个元素到集合类Collection dest对象中保存。
#### size()
BlockingQueue`size()` 方法返回队列中目前共有多少个元素
#### remainingCapacity()
BlockingQueue`remainingCapacity()` 方法将返回队列目前还剩多少个可用空间用于放入新的对象。剩余空间容量=队列的总容量-已经被占用的空间数量
- 线程
- 1.进程和线程-锁与信号量
- 2.Thread类线程状态转换
- 2.并发与并行-同步与异步
- 4.线程池
- 5.对象级别与类级别的同步锁
- 6.创建线程的四种方式
- 7.临界区-阻塞-活锁-死锁
- 2.JMM多线程模型
- JUC
- BlockingQueue
- ArrayBlockingQueue
- DelayQueue
- LinkedBlockingQueue
- PriorityBlockingQueue
- SynchronousQueue
- BlockingDeque
- ConcurrentHashMap
- CountDownLatch
- CyclicBarrier
- Exchanger
- AtomicInteger
- Lock
- Condition
- ReentrantLock读写锁
- StampedLock
- Semaphore