BlockingQueue 是一个先进先出的队列(Queue)java
BlockingQueue 支持当获取队列元素可是队列为空时,会阻塞等待队列中有元素再返回;数组
也支持添加元素时,若是队列已满,那么等到队列能够放入新元素时再放入。安全
BlockingQueue 对插入操做、移除操做、获取元素操做提供了四种不一样的方法用于不一样的场景中使用:数据结构
一、抛出异常;多线程
二、返回特殊值(null 或 true/false,取决于具体的操做);并发
三、阻塞等待此操做,直到这个操做成功;函数
四、阻塞等待此操做,直到成功或者超时指定时间。.net
BlockingQueue,咱们的关注点应该在 put(e) 和 take() 这两个方法,由于这两个方法是带阻塞的。线程
一个 BlockingQueue 多是有界的,若是在插入的时候,发现队列满了,那么 put 操做将会阻塞。设计
一般,在这里咱们说的无界队列也不是说真正的无界,而是它的容量是 Integer.MAX_VALUE(21亿多)。
BlockingQueue 是设计用来实现生产者-消费者队列的
BlockingQueue 的实现都是线程安全的,可是批量的集合操做如 addAll, containsAll, retainAll 和 removeAll 不必定是原子操做。如 addAll(c) 有可能在添加了一些元素后中途抛出异常,此时 BlockingQueue 中已经添加了部分元素,这个是容许的,取决于具体的实现。
BlockingQueue 不支持 close 或 shutdown 等关闭操做,由于开发者可能但愿不会有新的元素添加进去,此特性取决于具体的实现,不作强制约束。
ArrayBlockingQueue 是 BlockingQueue 接口的有界队列实现类,底层采用数组来实现。
其并发控制采用可重入锁来控制,不论是插入操做仍是读取操做,都须要获取到锁才能进行操做。
LinkedBlockingQueue底层基于单向链表实现的阻塞队列,能够当作无界队列也能够当作有界队列来使用。
它的实现用了两个锁,两个 Condition,简单介绍以下:
takeLock 和 notEmpty 怎么搭配:若是要获取(take)一个元素,须要获取 takeLock 锁,可是获取了锁还不够,若是队列此时为空,还须要队列不为空(notEmpty)这个条件(Condition)。
putLock 须要和 notFull 搭配:若是要插入(put)一个元素,须要获取 putLock 锁,可是获取了锁还不够,若是队列此时已满,还须要队列不是满的(notFull)这个条件(Condition)。
SynchronousQueue同步的队列。由于当一个线程往队列中写入一个元素时,写入操做不会当即返回,须要等待另外一个线程来将这个元素拿走;同理,当一个读线程作读操做的时候,一样须要一个相匹配的写线程的写操做。这里的 Synchronous 指的就是读线程和写线程须要同步,一个读线程匹配一个写线程。
SynchronousQueue 的队列实际上是虚的,其不提供任何空间(一个都没有)来存储元素。数据必须从某个写线程交给某个读线程,而不是写到某个队列中等待被消费。
Transferer 有两个内部实现类,是由于构造 SynchronousQueue 的时候,咱们能够指定公平策略。公平模式意味着,全部的读写线程都遵照先来后到,FIFO 嘛,对应 TransferQueue。而非公平模式则对应 TransferStack。
TransferQueue:(interface TransferQueue<E> extends BlockingQueue<E>) 也是一个阻塞队列,它具有阻塞队列的全部特性。LinkedTransferQueue
1.transfer(E e)若当前存在一个正在等待获取的消费者线程,即马上将e移交之;不然将元素e插入到队列尾部,而且当前线程进入阻塞状态,直到有消费者线程取走该元素。
2.tryTransfer(E e)若当前存在一个正在等待获取的消费者线程,则该方法会即刻转移e,并返回true;若不存在则返回false,可是并不会将e插入到队列中。这个方法不会阻塞当前线程,要么快速返回true,要么快速返回false。
3.hasWaitingConsumer()和getWaitingConsumerCount()用来判断当前正在等待消费的消费者线程个数。
4.tryTransfer(E e, long timeout, TimeUnit unit) 若当前存在一个正在等待获取的消费者线程,会当即传输给它; 不然将元素e插入到队列尾部,而且等待被消费者线程获取消费掉。若在指定的时间内元素e没法被消费者线程获取,则返回false,同时该元素从队列中移除。
https://blog.csdn.net/aitangyong/article/details/46472643
http://ifeve.com/java-transfer-queue/
PriorityBlockingQueue带排序的 BlockingQueue 实现,其并发控制采用的是 ReentrantLock,队列为无界队列(ArrayBlockingQueue 是有界队列,LinkedBlockingQueue 也能够经过在构造函数中传入 capacity 指定队列最大的容量,可是 PriorityBlockingQueue 只能指定初始的队列大小,后面插入元素的时候,若是空间不够的话会自动扩容)。
简单地说,它就是 PriorityQueue 的线程安全版本。
总结:
ArrayBlockingQueue 底层是数组,有界队列,若是咱们要使用生产者-消费者模式,这是很是好的选择。
LinkedBlockingQueue 底层是链表,能够当作无界和有界队列来使用,因此你们不要觉得它就是无界队列。
SynchronousQueue 自己不带有空间来存储任何元素,使用上能够选择公平模式和非公平模式。
PriorityBlockingQueue 是无界队列,基于数组,数据结构为二叉堆,数组第一个也是树的根节点老是最小值。
https://mp.weixin.qq.com/s/UlgoDIvR1FdpiKfbeeW0aw
LinkedBlockingQueue 与 ConcurrentLinkedQueue区别:
Concurrent*的容器真正表明并发, *BlockingQueue 表明阻塞。
Concurrent类型基于lock-free,在常见的多线程访问场景,能够提供较高吞吐量,但size是弱一致性,不够精准。
Concurrent类的没有CopyOnWrite类的容器有交重的修改开销。
BlockingQueue 内部基于锁,经过阻塞特性。