我们来看看使用这些方法的例子。 队列应该提供一个实现任务管理器的好方法,因为它们的主要目的是生成要处理的元素,比如任务。 目前我们将使用 ArrayDeque
作为队列中最快和最直接的实现(当然也是 Deque
)。 但是,像以前一样,我们只限于接口的方法 - 尽管您应该注意到,在选择队列实现时,您也正在选择一种排序。使用 ArrayDeque
,您可以获得 FIFO
订购 - 当然,我们尝试使用花哨的调度方法进行组织,似乎无法很好地工作; 也许是时候尝试一些简单的事情了。
ArrayDeque
是无限的,所以我们可以使用 add
或 offer
来设置新任务的队列。
Queue<Task> taskQueue = new ArrayDeque<Task>();
taskQueue.offer(mikePhone);
taskQueue.offer(paulPhone);
任何时候我们都准备好去做任务,我们可以选择一个达到队列头的人:
Task nextTask = taskQueue.poll();
if (nextTask != null) {
// process nextTask
}
使用民意调查和删除之间的选择取决于我们是否要将队列空虚视为特殊情况。 实际上 - 考虑到应用的性质 - 这可能是一个明智的假设,所以这是一个替代方案:
try {
Task nextTask = taskQueue.remove();
// process nextTask
} catch (NoSuchElementException e) {
// 但我们永远不会耗尽任务!
}
这种方案需要一些改进以适应不同类型任务的性质。电话任务适合相对较短的时间段,而我们不喜欢开始编码,除非有相当长的时间才能完成任务。 所以如果时间有限 - 比如说,直到下次会议 - 我们可能会希望在下班之前检查下一个任务是否正确。
Task nextTask = taskQueue.peek();
if (nextTask instanceof PhoneTask) {
taskQueue.remove();
// process nextTask
}
这些检查和删除方法是 Queue
界面的主要优点;集合没有像他们一样(虽然 NavigableSet
现在做)。我们为这个好处付出的代价是,只有头元素实际上是我们想要的元素时,Queue
的方法才对我们有用。诚然,类 PriorityQueue
允许我们提供一个比较器,它将排列队列元素,这样我们需要的队列元素就在头部,但这可能不是一种表达选择下一个任务的算法的特别好方法(例如,如果您需要了解所有未完成的任务,然后才能选择下一个)。所以在这种情况下,如果我们的待办事项管理者完全是基于队列的,我们最终可能会在开始会议前喝咖啡。作为替代方案,我们可以考虑使用List接口,它提供了访问其元素的更灵活的方法,但是它的缺点是它的实现对多线程的使用提供了更少的支持。
这听起来可能太悲观了;毕竟,Queue
是 Collection
的子接口,所以它继承了支持遍历的方法,比如迭代器。事实上,尽管实施了这些方法,但在正常情况下不建议使用这些方法。在队列类的设计中,遍历的效率已经与 Queue
方法的快速实现进行了交易;此外,队列迭代器不保证以正确的顺序返回它们的元素,并且对于某些并发队列,在正常条件下实际上会失败(请参见第 14.3.2
节)。
在下一节中,我们将看看 Queue
- Priority Queue
和 ConcurrentLinkedList
的直接实现 - 以及 14.3
章节中的 BlockingQueue
及其实现。这两节中的课程在行为上差异很大。他们大多数是线程安全的;大多数提供阻塞设施(即等待条件的操作适合他们执行);一些支持优先级排序;一个 DelayQueue
- 保留元素,直到它们的延迟已经过期,另一个 - 同步队列 - 完全是一个同步工具。在队列实现之间进行选择时,这些功能差异会比他们的表现更受影响。