1. Queue分类

1.1 BlockingQueue

BlockingQueue是并发包里阻塞队列的一个接口。主要有(针对JDK1.8版本)如下几种(下面的ConcurrentLinkedQueue和LinkedTransferQueue不是阻塞队列哦。):

队列 有界性 数据结构
ArrayBlockingQueue bounded 加锁 array list
LinkedBlockingQueue optionally-bounded 加锁 linked list
PriorityBlockingQueue unbounded 加锁 heap
DelayQueue unbounded 加锁 heap
SynchronizeQueue unbounded 加锁 linkedList

1.2 非阻塞的Queue

队列 有界性 数据结构
ConcurrentLinkedQueue unbounded 无锁 linked list
LinkedTransferQueue unbounded 无锁 linked list
  1. ConcurrentLinkedQueue是非阻塞队列(基于CAS实现了非阻塞算法),不实现BlockingQueue接口。如果有很多临界资源需要互斥访问,那么使用该类。
  2. LinkedTransferQueue算是

1.2 基本方法

BlockingQueue接口提供了:

3个添加元素方法(往队尾添加)。

  1. add:添加元素到队列里,添加成功返回true,由于容量满了添加失败会抛出IllegalStateException异常
  2. offer:添加元素到队列里,添加成功返回true,添加失败返回false。可以设定超时时间,再返回false。
  3. put:添加元素到队列里,如果容量满了会阻塞直到容量不满

PS: 建议少使用put方法,“快速失败”的处理策略更好点。

3个删除方法(从队首删除)。

  1. poll:删除队列头部元素,如果队列为空,返回null。否则返回元素。
  2. remove:基于对象找到对应的元素,并删除。删除成功返回true,否则返回false
  3. take:删除队列头部元素,如果队列为空,一直阻塞到队列有元素并删除


四组不同的行为方式解释:

  1. 抛异常:如果试图的操作无法立即执行,抛一个异常。
  2. 特定值:如果试图的操作无法立即执行,返回一个特定的值(常常是 true / false)。
  3. 阻塞:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行。
  4. 超时:如果试图的操作无法立即执行,该方法调用将会发生阻塞,直到能够执行,但等待时间不会超过给定值。返回一个特定值以告知该操作是否成功(典型的是true / false)。

2. ArrayBlockingQueue

2.1 实现细节

  1. 基于数组实现循环队列: 也就意味着预分配了空间,不过基于数组在性能上比基于链表的实现性能高些(CPU cache更友好吧)
  2. 在读写队列的时候都是用一个ReentrantLock对整个队列上锁(默认非公平锁,公平锁会线程饥饿)。见下图:

  1. 一旦创建,大小固定,有界队列。比较适合作为有界缓冲。大小固定,也就不必纠结空间回收的问题了,这个是优点也是缺点,看怎么理解了。
  2. 实现中使用了Condition来控制notFull和notEmpty条件: 例如take取数据的时候由于队列空了被阻塞;有新的数据put了,就可以signal那些take数据被阻塞的线程了。不利用Condition的话自己利用Object的notify、wait方法也可以做,不过显然麻烦多了。

2.2 应用场景

比较适合作为有界缓冲。

3. LinkedBlockingQueue

3.1 实现细节

  1. 基于链表实现: 大小可变,更加灵活。默认最大大小为Integer.MAX_VALUE
  2. 在队首(putLock)和队尾使用两把锁: 可以实现读写并发,吞吐性能比ArrayBlockingQueue好很多

  1. 也利用Condition来做非空非满的条件判断

3.2 应用场景

常见的不涉及大量互斥资源的生产消费的情况,都可以用该类实现生产消费模型

可以看看SF这个帖子增加理解:
when to prefer LinkedBlockingQueue over ArrayBlockingQueue

4. SynchronousQueue

4.1 说明

SynchronousQueue源码文档如下:

总结下有以下特点:

  1. 容量永远为0
  2. 适合一对一的生产消费场景,由于经过很多优化,性能是很好的。因此容量为1的队列,就别使用其他阻塞queue了
  3. 实现上也用了CAS、自旋锁

4.2 应用场景

SF上这个帖子说明了何时需要使用SynchronousQueueImplementation of BlockingQueue: What are the differences between SynchronousQueue and LinkedBlockingQueue

  1. 性能比前面几种Queue都要高
  2. 适合当做一个生产者和消费者之间的汇合点,传递数据。

5. DelayedQueue

5.1 原理

  1. 延迟队列内部使用优先队列管理任务
  2. 使用一个availabe Condition条件来做唤醒
  3. 使用的heap数据结构(因为用的优先队列是基于堆得)

5.2 应用场景

延迟任务、定期任务、周期任务

6. PriorityBlockingQueue

6.1 原理

  1. 存储的对象必须是实现Comparable接口
  2. 基于heap的数据机构(array-based binary heap)

6.2 应用场景

有优先级的任务

7. LinkedTransferQueue

7.1 原理

  1. 非阻塞
  2. 基于CAS无锁
  3. Doug Lea说从功能角度来讲,LinkedTransferQueue实际上是ConcurrentLinkedQueue、SynchronousQueue(公平模式)和LinkedBlockingQueue的超集。而且LinkedTransferQueue更好用,因为它不仅仅综合了这几个类的功能,同时也提供了更高效的实现。

7.2 应用场景

8. 关于队列选型

when to prefer LinkedBlockingQueue over ArrayBlockingQueue
参考资料:

  1. ConcurrentLinkedQueue、AraayBlockingQueue、LinkedBlockingQueue 区别及使用场景
  2. Java并发编程-阻塞队列(BlockingQueue)的实现原理
  3. 【JUC】JDK1.8源码分析之SynchronousQueue(九)
  4. 高性能队列——Disruptor