一.Queue 介绍
1.Queue 的介绍
Queue
,中文名叫队列
,无论现实生活中还是计算机的世界中,都是一个很重要的角色哦~
Queue 是一种数据结构
,大家可以把我想象成一个数组,元素从我的一头进入、从另外一头出去,称为 FIFO 原则(先进先出原则)。
Queue 有两个亲兄弟:List
(列表)、Set
(集),他们都是Collection
的儿子,Queue 还有一个远房亲戚:Map
(映射)。他们都是java.util
包这个大家庭的成员哦~
2.队列类图
18 个 Queue 的继承实现关系图
Queue
接口继承Collection
接口,Collection
接口继承Iterable
接口BlockingQueue
接口、Deque
接口 继承Queue
接口AbstractQueue
抽象类实现Queue
接口BlockingDeque
接口、TransferQueue
接口继承BlockingQueue
接口BlockingDeque
接口继承Deque
接口LinkedBlockingDeque
类实现BlockingDeque
接口LinkedTransferQueue
类接口实现TransferQueue
接口LinkedList
类、ArrayDeque
类、ConcurrentLinkedDeque
类实现 了Deque
接口ArrayBlockingQueue
类、LinkendBlockingQueue
类、LinkedBlockingDeque
类、LinkedTransferQueue
类、SynchronousQueue
类、PriorityBlockQueue
类、DelayQueue类
继承 了AbstractQueue
抽象类和实现了 BlockingQueue 接口PriorityQueue
类和ConcurrentLinkedQueue
类继承 了AbstractQueue
抽象类
注意:
- Deque:全称 Double-Ended queue,表示双端队列。
- 类实现接口,用 implements
- 接口继承接口,用 extends
- 类继承类,用 extends
3.Queue 的核心方法
- add
- element
- offer
- peek
- poll
- remove
Queue的核心方法:
动作 | 抛异常 | 返回特殊值 |
Insert | add(e) | offer(e) |
Remove | remove() | poll |
Examine | element() | peek() |
- 比如
添加(Insert)
元素的动作,会有两种方式:add(e)
和offer(e)
。如果调用 add(e)方法时,添加失败,则会抛异常
,而如果调用的是 offer(e)方法失败时,则会返回false
。offer 方法用于异常是正常的情况下使用,比如在有界队列中,优先使用 offer 方法。假如队列满了,不能添加元素,offer 方法返回 false,这样我们就知道是队列满了,而不是去 handle 运行时抛出的异常。 - 同理,移除(Remove)元素的动作,队列为空时,remove 方法抛异常,而 poll 返回 null。如果移除头部的元素成功,则返回移除的元素。
- 同理,检测(Examine)元素的动作,返回头部元素(最开始加入的元素),但不删除元素, 如果队列为空,则 element()方法抛异常,而 peek()返回 false。
- Queue 接口没有定义阻塞队列的方法,这些方法在 BlockQueue 接口中定义了。
- Queue 实现类通常不允许插入 null 元素,尽管一些实现类比如 LinkedList 不禁止插入 null,但是还是不建议插入 null,因为 null 也被用在 poll 方法的特殊返回值,以说明队列不包含元素。
4.Queue 的实现类
5.常见 Queue 说明
二.Deque 接口
1.什么是 Deque?
Queue 以及 Deque 都是继承于 Collection,Deque 是 Queue 的子接口。双端队列,可以在首尾插入或删除元素。
Deque 中直接子类有两个:LinkedList 以及 ArrayDeque。
ArrayDeque 是无初始容量的双端队列,LinkedList 则是双向链表。而我们还能看到,ArrayDeque 作为队列时的效率比 LinkedList 要高,而在栈的使用场景下,无疑具有尾结点不需判空的 LinkedList 较高效。
ArrayDeque 通常作为栈或队列使用,但是栈的效率不如 LinkedList 高。LinkedList 通常作为栈或队列使用,但是队列的效率不如 ArrayQueue 高。
2.双端可用 Deque 接口
Deque 概念: 支持两端元素插入和移除的线性集合。名称deque
是双端队列的缩写,通常发音为deck
。大多数实现 Deque 的类,对它们包含的元素的数量没有固定的限制的,支持有界和无界。
3.Deque 方法
4.Deque 和 Queue 比较
作为 FIFO 时等价于 Queue 的方法如下表所示:
比如 Queue 的 add 方法和 Deque 的 addLast 方法等价。
5.Deque 和 Stack 比较
- Deque 也可以用作 LIFO(后进先出)栈,这个接口优于传统的 Stack 类。当作为栈使用时,元素被 push 到 deque 队列的头,而 pop 也是从队列的头 pop 出来。
- Stack(栈)的方法正好等同于 Deque 的如下方法:
注意:peek 方法不论是作为栈还是队列,都是从队列的检测队列的头,返回最先加入的元素。比如第一次 put 100,第二次 put 200,则 peek 返回的是 100。
6.Deque 的实现与继承
哪些类实现了Deque接口:
- LinkedList 类
- ArrayDeque 类
- ConcurrentLinkedDeque 类
- LinkedBlockingDeque 类
哪些类继承了Deque接口:
- BlockingDeque 接口
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {}
三.AbstractQueue 类
1.AbstractQueue 类介绍
AbstractQueue 是一个抽象类,实现了 Queue 接口,提供了一些 Queue 操作的骨架实现。
public abstract class AbstractQueue<E>
extends AbstractCollection<E>
implements Queue<E> {
}
2.AbstractQueue 的方法
- add
- addAll
- clear
- element
- remove
方法 add、remove、element 方法基于 offer、poll 和 peek。也就是说如果不能正常操作,则抛出异常。
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
如果继承 AbstractQueue 抽象类则必须保证 offer 方法不允许 null 值插入。
3.AbstractQueue 的继承
哪些类继承了 AbstractQueue 抽象类:
ArrayBlockingQueue
类LinkendBlockingQueue
类LinkedBlockingDeque
类LinkedTransferQueue
类SynchronousQueue
类PriorityBlockQueue
类DelayQueue
类PriorityQueue
类ConcurrentLinkedQueue
类
四.BlockingQueue 接口
1.BlockingQueue 接口定义
阻塞队列:
- BlockQueue 满了,PUT 操作被阻塞
- BlockQueue 为空,Take 操作被阻塞
阻塞队列为空的情况:
- BlockingQueue(阻塞队列)也是一种队列,支持阻塞的插入和移除方法。
- 阻塞的插入:当队列满时,队列会阻塞插入元素的线程,直到队列不满。
- 阻塞的移除:当队列为空,获取元素的线程会等待队列变为非空。
- 应用场景:生产者和消费者,生产者线程向队列里添加元素,消费者线程从队列里移除元素,阻塞队列时获取和存放元素的容器。
- 为什么要用阻塞队列:生产者生产和消费者消费的速率不一样,需要用队列来解决速率差问题,当队列满了或空的时候,则需要阻塞生产或消费动作来解决队列满或空的问题。
2.BlockingQueue 核心方法
BlockingQueue 接口的 10 个核心方法:
3.十个核心方法总结如下
BlockingQueue接口的10个核心方法:
有三大类操作:插入、移除、检查。
- 插入有四种方法: add、offer、put、offer 超时版。
IllegalStateException
- 队列满了ClassCastException
- 添加的元素类型不匹配NullPointerException
- 添加的元素为 nullIllegalArgumentException
- 添加的元素某些属性不匹配- add 方法特别之处用于添加失败时抛出异常,共有四种异常:
- offer 方法特别之处用于添加失败时只返回 false
- put 方法特别之处用于当阻塞队列满时,生产者如果往队列里 put 元素,则队列会一直阻塞生产者线程,直到队列可用或者响应中断退出
- offer 超时方法特别之处用于当阻塞队列满时,生产者如果往队列里面插入元素,队列会阻塞生产者线程一段时间,如果超过了指定时间,生产者线程会退出,并返回 false。
- 移除有四种方法: remove、poll、take、poll 超时版
NoSuchElementException
- 如果这个队列是空的- remove 方法特别之处用于移除失败时抛出异常
- poll 方法特别之处用于移除失败时返回 null
- take 方法特别之处用于当阻塞队列为空时,消费者线程如果从队列里面移除元素,则队列会一直阻塞消费者线程,直到队列不为空
- poll 超时方法特别之处用于当阻塞队列空时,消费者如果从队列里面删除元素,则队列会一直阻塞消费者线程,如果超过了指定时间,消费者线程会退出,并返回 null
- 检查有两种方法: element、peek
- element 方法用于检测头部元素的存在性,如果队列为空,则抛出异常,否则返回头部元素。
- peek 方法用于检测头部元素的存在性,如果队列为空,返回特殊值 null,否则返回头部的元素。
4.BlockingQueue 原理
BlockingQueue 通过什么来阻塞插入和移除的?
- 当往队列里插入一个元素时,如果队列不可用,那么阻塞生产者主要通过 LockSupport. park(this)来实现。
- park 这个方法会阻塞当前线程,只有以下 4 种情况中的一种发生时,该方法才会返回。
- 与 park 对应的 unpark 执行或已经执行时。“已经执行”是指 unpark 先执行,然后再执行 park 的情况。
- 线程被中断时。
- 等待完 time 参数指定的毫秒数时。
- 异常现象发生时,这个异常现象没有任何原因。
5.BlockingQueue 继承与实现
哪些类继承了BlockingQueue接口:
- BlockingDeque 接口 - 双端阻塞队列
- TransferQueue 接口 - 传输队列
哪些类实现了BlockingQueue接口:
- ArrayBlockingQueue 类 - 由数组构成的有界阻塞队列
- LinkedBlockingQueue 类 - 由链表构成的有界阻塞队列,界限默认大小为 Integer.MAX_Value(2^31-1),值非常大,相当于无界。
- LinkedBlockingDeque 类 - 由链表构成的双向阻塞队列
- LinkedTransferQueue 类 - 由链表构成的无界阻塞队列
- SynchronousQueue 类 - 不存储元素的阻塞队列,只有一个元素进行数据传递。
- LinkedTransferQueue 类 - 由链表构成的无界阻塞 TransferQueue 队列
- DelayQueue 类 - 使用优先级队列实现的延迟无界阻塞队列
6.BlockingDeque 接口方法
是阻塞队列BlockingQueue
和双向队列Deque
接口的结合。有如下方法:
BlockingQueue和BlockingDeque的对等方法:
五.TransferQueue 接口
1.TransferQueue 简介
如果有消费者正在获取元素,则将队列中的元素传递给消费者。如果没有消费者,则等待消费者消费。我把它称作使命必达队列,必须将任务完成才能返回。
2.理解三个方法
针对TransferQueue的transfer方法:
圆通快递员要将小明的 2 个快递送货到门,韵达快递员也想将小明的 2 个快递送货到门。小明一次只能拿一个,快递员必须等小明拿了一个后,才能继续给第二个。
针对TransferQueue的tryTransfer方法:
圆通快递员要将小明的 2 个快递送货到门,韵达快递员也想将小明的 2 个快递送货到门。发现小明不在家,就把快递直接放到菜鸟驿站了。
针对TransferQueue的tryTransfer超时方法:
圆通快递员要将小明的 2 个快递送货到门,韵达快递员也想将小明的 2 个快递送货到门。发现小明不在家,于是先等了 5 分钟,发现小明还没有回来,就把快递直接放到菜鸟驿站了。
3.TransferQueue 的原理解析
transfer方法的原理:
- 原理图解释:生产者线程 Producer Thread 尝试将元素 B 传给消费者线程,如果没有消费者线程,则将元素 B 放到尾节点。并且生产者线程等待元素 B 被消费。当元素 B 被消费后,生产者线程返回。
- 如果当前有消费者正在等待接收元素(消费者通过 take 方法或超时限制的 poll 方法时),transfer 方法可以把生产者传入的元素立刻 transfer(传输)给消费者。
- 如果没有消费者等待接收元素,transfer 方法会将元素放在队列的 tail(尾)节点,并等到该元素被消费者消费了才返回。
tryTransfer(E e)
- 试探生产者传入的元素是否能直接传给消费者。
- 如果没有消费者等待接收元素,则返回 false。
- 和 transfer 方法的区别是,无论消费者是否接收,方法立即返回。
tryTransfer(E e, long timeout, TimeUnit unit)
- 带有时间限制的 tryTransfer 方法。
- 试图把生产者传入的元素直接传给消费者。
- 如果没有消费者消费该元素则等待指定的时间再返回。
- 如果超时了还没有消费元素,则返回 false。
- 如果在超时时间内消费了元素,则返回 true。
getWaitingConsumerCount()
- 获取通过 BlockingQueue.take()方法或超时限制 poll 方法等待接受元素的消费者数量。近似值。
- 返回等待接收元素的消费者数量。
hasWaitingConsumer()
- 获取是否有通过 BlockingQueue.tabke()方法或超时限制 poll 方法等待接受元素的消费者。
- 返回 true 则表示至少有一个等待消费者。
4.TransferQueue 接口实现与继承
TransferQueue接口继承了哪些接口:
- BlockingQueue 接口,可作为阻塞队列使用
- Queue 接口,可作为队列使用
哪些类实现了TransferQueue接口:
- LinkedTranferQueue 接口
六.PriorityQueue 类
1.什么是 PriorityQueue?
队列是遵循先进先出(First-In-First-Out)模式的,但有时需要在队列中基于优先级处理对象。
Queue 有一个直接子类 PriorityQueue
PriorityQueue 是基于优先堆的一个无界队列,这个优先队列中的元素可以默认自然排序或者通过提供的 Comparator(比较器)在队列实例化的时排序。
- 优先队列的头是基于自然排序或者 Comparator 排序的最小元素。如果有多个对象拥有同样的排序,那么就可能随机地取其中任意一个。当我们获取队列时,返回队列的头对象。
- 优先队列的大小是不受限制的,但在创建时可以指定初始大小。当我们向优先队列增加元素的时候,队列大小会自动增加。
- PriorityQueue 是非线程安全的,所以 Java 提供了 PriorityBlockingQueue(实现 BlockingQueue 接口)用于 Java 多线程环境。
PriorityQueue 的底层数据结构是数组,而无边界的形容,那么指明了 PriorityQueue 是自带扩容机制的,具体请看 PriorityQueue 的 grow 方法。
PriorityQueue 可以作为堆使用,而且可以根据传入的 Comparator 实现大小的调整,会是一个很好的选择。
2.PriorityQueue 特点
按照自定义优先级排序
- PriorityQueue 是一个支持优先级的无界阻塞队列;
- 默认自然顺序升序排序;
- 可以通过构造参数 Comparator 来对元素进行排序;
- 自定义实现 comapreTo()方法来指定元素排序规则。
- 不允许插入 null 元素。
- 实现 PriorityQueue 接口的类,不保证线程安全,除非是 PriorityBlockingQueue。
- PriorityQueue 的迭代器不能保证以任何特定顺序遍历元素,如果需要有序遍历,请考虑使用
Arrays.sort(pq.toArray)
。 - 进列(
offer
、add
)和出列(poll
、remove()
)的时间复杂度 O(log(n))。 - remove(Object) 和 contains(Object)的算法时间复杂度 O(n)。
- peek、element、size 的算法时间复杂度为 O(1)。
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
3.PriorityQueue 继承与实现
PriorityQueue类继承了哪些类:
- AbstractQueue 抽象类,具有队列的功能
PriorityQueue类实现了哪些接口:
- Queue 接口,可作为队列使用。
七.并发安全 Queue
1.ConcurrentLinkedQueue 图解
2.ConcurrentLinkedQueue 原理
- ConcurrentLinked 是由链表结构组成的线程安全的先进先出无界队列。
- 当多线程要共享访问集合时,ConcurrentLinkedQueue 是一个比较好的选择。
- 不允许插入 null 元素
- 支持非阻塞地访问并发安全的队列,不会抛出 ConcurrentModifiationException 异常。
- size 方法不是准确的,因为在统计集合的时候,队列可能正在添加元素,导致统计不准。
- 批量操作 addAll、removeAll、retainAll、containsAll、equals 和 toArray 不保证原子性(操作不可分割)
- 添加元素 happen-before 其他线程移除元素。
用法如下:
ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
BuildingBlockWithName buildingBlock = new BuildingBlockWithName("三角形", "A");
concurrentLinkedQueue.add(buildingBlock);
继承与实现
ConcurrentLinkedQueue 类继承了哪些类
- AbstractQueue 抽象类,具有队列的功能
ConcurrentLinkedQueue 类实现了哪些接口
- Queue 接口,可作为队列使用
3.ConcurrentLinkedDeque 类
ConcurrentLinkedDeque原理图:
- 由链表结构组成的双向无界阻塞队列
- 插入、删除和访问操作可以并发进行,线程安全的类
- 不允许插入 null 元素
- 在并发场景下,计算队列的大小是不准确的,因为计算时,可能有元素加入队列。
- 批量操作 addAll、removeAll、retainAll、containsAll、equals 和 toArray 不保证原子性(操作不可分割)
使用:
BuildingBlockWithName buildingBlock1 = new BuildingBlockWithName("三角形", "A");
BuildingBlockWithName buildingBlock2 = new BuildingBlockWithName("四边形", "B");
ConcurrentLinkedDeque concurrentLinkedDeque = new ConcurrentLinkedDeque();
concurrentLinkedDeque.addFirst(buildingBlock1);
concurrentLinkedDeque.addLast(buildingBlock2);
//结果:顺序:三角形、四边形
4.ArrayBlockingQueue 类
ArrayBlockingQueuey原理:
- ArrayBlockingQueue 是一个用数组实现的有界阻塞队列。
- 队列慢时插入操作被阻塞,队列空时,移除操作被阻塞。
- 按照先进先出(FIFO)原则对元素进行排序。
- 默认不保证线程公平的访问队列。
- 公平访问队列:按照阻塞的先后顺序访问队列,即先阻塞的线程先访问队列。
- 非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格。有可能先阻塞的线程最后才访问访问队列。
- 公平性会降低吞吐量。
使用:
创建两个积木:三角形、四边形,然后添加到队列:
BuildingBlockWithName buildingBlock1 = new BuildingBlockWithName("三角形", "A");
BuildingBlockWithName buildingBlock2 = new BuildingBlockWithName("四边形", "B");
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(100, true);
arrayBlockingQueue.add(buildingBlock1);
arrayBlockingQueue.add(buildingBlock2);
5.LinkedBlockingQueue 类
LinkedBlockingQueue原理:
- LinkedBlockingQueue 具有单链表和有界阻塞队列的功能。
- 队列慢时插入操作被阻塞,队列空时,移除操作被阻塞。
- 默认和最大长度为 Integer.MAX_VALUE,相当于无界(值非常大:2^31-1)。
LinkedBlockingQueue的应用场景:
- 吞吐量通常要高于 ArrayBlockingQueue。创建线程池时,参数 runnableTaskQueue(任务队列),用于保存等待执行的任务的阻塞队列可以选择 LinkedBlockingQueue。静态工厂方法 Executors.newFixedThreadPool()使用了这个队列。
LinkedBlockingQueue实现了哪些接口:
- LinkedBlockingQueue 继承了 BlockingQueue 类,可作为阻塞队列使用
- LinkedBlockingQueue 继承了 AbstractQueue 抽象类,具有队列的功能。
- LinkedBlockingQueue 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
6.LinkedBlockingDeque 类
LinkedBlockingDeque原理:
- 由链 LinkedBlockingDeque = 阻塞队列+链表+双端访问
- 线程安全。
- 多线程同时入队时,因多了一端访问入口,所以减少了一半的竞争。
- 默认容量大小为 Integer.MAX_VALUE。可指定容量大小。
LinkedBlockingDeque的应用场景:
LinkedBlockingDeque 可以用在“工作窃取“模式中。
工作窃取算法
:某个线程比较空闲,从其他线程的工作队列中的队尾窃取任务来帮忙执行。
LinkedBlockingDeque实现了哪些接口:
- LinkedBlockingDeque 继承了 BlockingDeque 类,可作为阻塞队列使用
- LinkedBlockingDeque 继承了 AbstractQueue 抽象类,具有队列的功能。
- LinkedBlockingDeque 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
7.PriorityBlockingQueue 类
PriorityBlockQueue的原理:
- PriorityBlockQueue = PriorityQueue + BlockingQueue
- 之前我们也讲到了 PriorityQueue 的原理,支持对元素排序。
- 元素默认自然排序。
- 可以自定义 CompareTo()方法来指定元素排序规则。
- 可以通过构造函数构造参数 Comparator 来对元素进行排序。
PriorityBlockQueue实现了哪些接口:
- LinkedBlockingQueue 继承了 BlockingQueue 接口,可作为阻塞队列使用
- LinkedBlockingQueue 继承了 AbstractQueue 抽象类,具有队列的功能。
- LinkedBlockingQueue 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。
八.其他 Queue
1.ArrayDeque 类
特点:
- 由数组组成的双端队列。
- 没有容量限制,根据需要扩容。
- 不是线程安全的。
- 禁止插入 null 元素。
- 当用作栈时,比栈速度快,当用作队列时,速度比 LinkList 快。
- 大部分方法的算法时间复杂度为 O(1)。
- remove、removeFirstOccurrence、removeLastOccurrence、contains、remove 和批量操作的算法时间复杂度 O(n)
使用:
ArrayDeque arrayDeque = new ArrayDeque();
for (int i = 0; i < 50; i++) {
arrayDeque.add(buildingBlock); // add方法等价于addLast方法
}
2.LinkedTransferQueue 类
LinkedTransferQueue原理:
LinkedTransferQueue = 阻塞队列+链表结构+TransferQueue
之前我们讲“使命必达 TransferQueue 接口时已经介绍过了 TransferQueue 接口 ,所以 LinkedTransferQueue 接口跟它相似,只是加入了阻塞插入和移除的功能,以及结构是链表结构。
之前的 TransferQueue 也讲到了 3 个案例来说明 TransferQueue 的原理,大家可以回看 TransferQueue。
LinkedTransferQueue接口比其他阻塞队列多了5个方法:
- transfer(E e)
- tryTransfer(E e)
- tryTransfer(E e, long timeout, TimeUnit unit)
- getWaitingConsumerCount()
- hasWaitingConsumer()
LinkedTransferQueue实现了哪些接口:
- LinkedBlockingDeque 继承了 BlockingQeque 类,可作为阻塞队列使用
- LinkedBlockingDeque 继承了 AbstractQueue 抽象类,具有队列的功能
3.SynchronousQueue 类
SynchronousQueue原理:
- 我称 SynchronousQueue 为”传球好手“。想象一下这个场景:小明抱着一个篮球想传给小花,如果小花没有将球拿走,则小明是不能再拿其他球的。
- SynchronousQueue 负责把生产者产生的数据传递给消费者线程。
- SynchronousQueue 本身不存储数据,调用了 put 方法后,队列里面也是空的。
- 每一个 put 操作必须等待一个 take 操作完成,否则不能添加元素。
- 适合传递性场景。
- 性能高于 ArrayBlockingQueue 和 LinkedBlockingQueue。
SynchronousQueue应用场景:
- 吞吐量通常要高于 LinkedBlockingQueue。创建线程池时,参数 runnableTaskQueue(任务队列),用于保存等待执行的任务的阻塞队列可以选择 SynchronousQueue。静态工厂方法 Executors.newCachedThreadPool()使用了这个队列
SynchronousQueue和LinkedTransferQueue的区别:
- SynchronousQueue 不存储元素,而 LinkedTransferQueue 存储元素。
- SynchronousQueue 队列里面没有元素,而 LinkedTransferQueue 可以有多个存储在队列等待传输。
- LinkedTransferQueue 还支持若传输不了,则丢到队列里面去。
- LinkedTransferQueue 还支持若超过一定时间传输不了,则丢到队列里面去。
4.DelayQueue 类
DelayQueue原理:
- DelayQueue = Delayed + BlockingQueue。队列中的元素必须实现 Delayed 接口。
- 在创建元素时,可以指定多久可以从队列中获取到当前元素。只有在延时期满才能从队列中获取到当前元素。
public class DelayQueue<E extends Delayed> extends AbstractQueue<E>
implements BlockingQueue<E> {
源码解析:
- 添加元素时,指定延时多久可以从队列中获取元素
public boolean offer(E e, long timeout, TimeUnit unit) {
return offer(e);
}
- 获取元素的方法 poll 需要等待延时时间过了才能获取到元素
if (first == null || first.getDelay(NANOSECONDS) > 0)
return null;
else
return q.poll();
应用场景:
- 缓存系统的设计:可以用 DelayQueue 保存缓存元素的有效期。然后用一个线程循环的查询 DelayQueue 队列,一旦能从 DelayQueue 中获取元素时,表示缓存有效期到了。
- 定时任务调度:使用 DelayQueue 队列保存当天将会执行的任务和执行时间,一旦从 DelayQueue 中获取到任务就开始执行。比如 Java 中的 TimerQueue 就是使用 DelayQueue 实现的。
DelayQueue实现了哪些接口:
- DelayQueue 实现了 BlockingQueue 接口,可作为阻塞队列使用