一、什么是 queue(队列) ?
在数据结构中,队列的解释是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。故队列又称为先进先出(FIFO——first in first out)的线性表。
二、queue 的实现和继承
Queue 和 List 、Set 一样都继承自 Collection 接口,其中我们经常用到的 LinkedList 实现了 Queue 接口。
而在并发队列上, JDK 提供了两套实现:一个就是以 ConcurrentLinkedQueue 为代表的高性能的非阻塞队列;一个是以 BlockingQueue 为代表的阻塞队列,例如:ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue等。这两种都实现或继承自 Queue。
Queue接口方法定义:
//添加元素,成功返回true,容量不够抛IllegalStateException
boolean add(E e)
//添加元素,成功返回true,容量不足返回false
boolean offer(E e)
//移除队首元素,队列为空时抛NoSuchElementException
E remove()
//移除队首元素,队列为空时返回null
E poll()
//查看队首元素,队列为空时抛NoSuchElementException
E element()
//查看队首元素,队列为空时返回null
E peek()
非阻塞:ConcurrentLinkedQueue
ConcurrentLinkedQueue 是一个适用于高并发场景下的队列,通过无锁的方式实现了高并发状态下的高性能,通常ConcurrentLinkedQueue 的性能好于 BlockingQueue 。它是一个基于链接节点的无界线程安全队列,其中ConcurrentLinkedQueue通过 sun.misc.Unsafe
类的CAS操作来保证线程安全。该队列的元素遵循先进先出的原则,该队列不允许 null 元素,如果存入 null ,则会抛出 NullPointerException 异常。
ConcurrentLinkedQueue 的继承关系:
ConcurrentLinkedQueue 的重要方法:
- add() 增加一个元素 如果队列已满,则抛出一个IIIegaISlabEepeplian异常
- offer() 同 add 方法
- poll() 移除并返问队列头部的元素 如果队列为空,则返回null
- peek() 返回队列头部的元素 如果队列为空,则返回null
public class ConcurrentLinkedQueueTest {
public static void main(String[] args) {
ConcurrentLinkedQueue c = new ConcurrentLinkedQueue();
c.add("a");
c.add("b");
c.add("c");
c.add("d");
c.offer("e");
System.out.println(c.poll()); //移除并取出元素
System.out.println(c.size());
System.out.println(c.peek()); //只取出不移除元素
System.out.println(c.size());
}
}
返回结果:
a
4
b
4
Process finished with exit code 0
阻塞:BlockingQueue
BlockingQueue 的重要方法:
//添加元素,容量不足阻塞
void put(E e) throws InterruptedException
//添加元素,容量不足时等待指定时间
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException
//移除队首元素,队列为空时阻塞
E take() throws InterruptedException
//移除队首元素,队列为空时等待指定时间
E poll(long timeout, TimeUnit unit) throws InterruptedException
其中有五个阻塞队列类实现了BlockingQueue 接口。
- ArrayBlockingQueue :一个由数组支持的有界队列。
- LinkedBlockingQueue :一个由链接节点支持的可选有界队列。
- PriorityBlockingQueue :一个由优先级堆支持的无界优先级队列。
- DelayQueue :一个由优先级堆支持的、基于时间的调度队列。
- SynchronousQueue :一个利用 BlockingQueue 接口的简单聚集(rendezvous)机制。
ArrayBlockingQueue:基于数组的阻塞队列实现,在 ArrayBlockingQueue 内部,维护了一个定长数组,以便缓存队列中的数据对象,其内部没有实现读写分离,也就意味着生产和消费不能完全并行,长度是需要定义的,可以指定先进先出或者先进后出,也叫有界队列,在很多场合非常适用。
LinkedBlockingQueue :基于链表的阻塞队列,同ArrayBlockingQueue 类似,其内部也维持着一个数据缓冲队列(该队列由一个链表构成),LinkedBlockingQueue 之所以能够高效的处理并发数据,是因为其内部采用分离锁(读写分离两个锁),从而实现生产者和消费者操作的完全并行运行,它是一个无界队列(指的是没有设置固定大小的队列,这些队列的特点是可以直接入列,直到溢出。)。
PriorityBlockingQueue :基于优先级的阻塞队列(优先级的判断通过构造函数传入的 Compator 对象来决定,也就是说传入队列的对象必须实现 Comparable 接口),在实现 PriorityBlockingQueue 时,内部控制线程同步的锁采用的是公平锁,它也是一个无界队列。
- 公平锁:加锁前先查看是否有排队等待的线程,有的话优先处理排在前面的线程,先来先得。
- 非公平所:线程加锁时直接尝试获取锁,获取不到就自动到队尾等待。
DelayQueue :带有延迟时间的 Queue,其中的元素只有当其指定的延迟时间到了,才能从队列中获取到该元素。DelayQueue 中的元素必须实现 Delayed 接口, DelayQueue 是一个没有大小限制的队列,应用场景很多,比如对缓存超时数据进行移除、任务超时处理、空闲连接的关闭等等。
SynchronousQueue:一种没有缓冲的队列,生产者产生的数据直接会被消费者获取并消费。