Java Queue 集合详解
Queue是Java集合框架中用于处理元素队列的核心接口,它继承自Collection接口,提供了先进先出(FIFO)的数据结构。在Java开发中,Queue集合被广泛应用于任务调度、消息传递、生产者-消费者模式等各种场景,是并发编程和异步处理的重要基础。
Queue接口 = 先进先出 + 阻塞操作支持 + 优先级排序 + 线程安全 + 并发控制
- 🚶♂️ 先进先出(FIFO) - 元素按照插入顺序被处理,遵循"先来先服务"原则
- ⏳ 阻塞操作 - 支持在队列满或空时自动等待,简化并发编程
- 🥇 优先级支持 - 可基于元素优先级而非插入顺序进行处理
- 🔒 线程安全 - 提供多种线程安全实现,适用于并发环境
- 🔄 双端队列 - 通过Deque接口扩展,支持两端插入和删除操作
1. Queue接口基础概念
1.1 什么是Queue接口?
Queue接口是Java集合框架中的核心接口,它继承自Collection接口,为队列操作提供了完整的抽象。下面是Queue接口的继承层次结构和主要实现类:
Queue集合具有以下核心特征:
- 先进先出:元素按照插入顺序处理,最先插入的元素最先被处理
- 阻塞操作:某些实现类支持阻塞操作,适合并发环境
- 优先级排序:某些实现类支持按优先级排序
- 线程安全:某些实现类提供线程安全保证
- 并发控制:支持生产者-消费者模式
1.2 Queue接口的重要性
| 重要性 | 具体体现 | 业务价值 |
|---|---|---|
| 任务调度 | 提供有序的任务处理机制 | 支持系统任务的有序执行 |
| 消息传递 | 实现异步消息处理 | 提高系统响应性能 |
| 并发控制 | 支持多线程间的数据传递 | 简化并发编程复杂度 |
| 资源管理 | 控制资源访问顺序 | 避免资源竞争和死锁 |
1.3 Queue接口设计原则
Queue接口的设计遵循以下几个核心原则:
FIFO原则
提供先进先出的元素处理顺序,保证处理的公平性
阻塞原则
支持阻塞操作,在队列满或空时自动等待
优先级原则
支持按优先级排序,满足不同业务场景需求
并发原则
提供线程安全的操作,支持多线程环境
1public interface Queue<E> extends Collection<E> {2 3 // ========== 添加操作 ==========4 boolean add(E e); // 添加元素,失败时抛出异常5 boolean offer(E e); // 添加元素,失败时返回false6 7 // ========== 删除操作 ==========8 E remove(); // 删除并返回头部元素,失败时抛出异常9 E poll(); // 删除并返回头部元素,失败时返回null10 11 // ========== 查看操作 ==========12 E element(); // 查看头部元素,失败时抛出异常13 E peek(); // 查看头部元素,失败时返回null14}1.4 Queue接口方法分类详解
Queue接口提供了丰富的方法来操作队列,这些方法可以分为几个主要类别:
- 添加操作方法
- 删除操作方法
- 查看操作方法
- 集合操作方法
1public interface Queue<E> extends Collection<E> {2 3 // 添加元素到队列尾部4 boolean add(E e); // 添加元素,队列满时抛出IllegalStateException5 boolean offer(E e); // 添加元素,队列满时返回false6 7 // 继承自Collection的方法8 boolean addAll(Collection<? extends E> c); // 批量添加元素9}| 方法 | 描述 | 失败时行为 | 适用场景 |
|---|---|---|---|
add(E) | 添加元素到队列尾部 | 抛出异常 | 希望立即知道操作是否成功 |
offer(E) | 添加元素到队列尾部 | 返回false | 对操作失败有特殊处理逻辑 |
addAll(Collection) | 批量添加元素 | 部分添加并抛出异常 | 需要批量操作时 |
对于BlockingQueue实现,还提供了额外的添加方法:
put(E):添加元素,如果队列已满则阻塞等待offer(E, long, TimeUnit):添加元素,如果队列已满则阻塞等待指定时间
1BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);2// 阻塞添加3queue.put("元素"); // 如果队列已满,会阻塞等待4// 限时添加5boolean success = queue.offer("元素", 5, TimeUnit.SECONDS); // 最多等待5秒1public interface Queue<E> extends Collection<E> {2 3 // 删除并返回队列头部元素4 E remove(); // 删除元素,队列空时抛出NoSuchElementException5 E poll(); // 删除元素,队列空时返回null6 7 // 删除指定元素8 boolean remove(Object o); // 删除指定元素,继承自Collection9 boolean removeAll(Collection<?> c); // 批量删除元素,继承自Collection10}| 方法 | 描述 | 队列为空时行为 | 适用场景 |
|---|---|---|---|
remove() | 移除并返回队列头部元素 | 抛出异常 | 确信队列非空时 |
poll() | 移除并返回队列头部元素 | 返回null | 队列可能为空时 |
remove(Object) | 移除特定元素(若存在) | 返回操作结果 | 按值删除而非位置 |
clear() | 清空整个队列 | 队列变为空 | 需要重置队列时 |
对于BlockingQueue实现,还提供了额外的删除方法:
take():获取并移除队列头部元素,如果队列为空则阻塞等待poll(long, TimeUnit):获取并移除队列头部元素,如果队列为空则阻塞等待指定时间
1BlockingQueue<String> queue = new LinkedBlockingQueue<>();2// 阻塞删除3String element = queue.take(); // 如果队列为空,会阻塞等待4// 限时删除5String element2 = queue.poll(3, TimeUnit.SECONDS); // 最多等待3秒1public interface Queue<E> extends Collection<E> {2 3 // 查看队列头部元素但不删除4 E element(); // 查看元素,队列空时抛出NoSuchElementException5 E peek(); // 查看元素,队列空时返回null6}| 方法 | 描述 | 队列为空时行为 | 适用场景 |
|---|---|---|---|
element() | 获取但不移除队列头部元素 | 抛出异常 | 确信队列非空时 |
peek() | 获取但不移除队列头部元素 | 返回null | 队列可能为空时 |
查看操作与删除操作的主要区别在于:
- 查看操作只是获取元素引用,不会修改队列结构
- 删除操作会将元素从队列中移除
- 在并发环境中,查看后再删除的操作不是原子的,可能会导致不一致
1Queue<Integer> queue = new LinkedList<>();2queue.add(1);3queue.add(2);45// 查看元素但不删除6Integer head1 = queue.peek(); // 返回1,队列仍有[1,2]7// 使用element方法8Integer head2 = queue.element(); // 返回1,队列仍有[1,2]910// 删除操作会修改队列11Integer removed = queue.poll(); // 返回1,队列变为[2]1public interface Queue<E> extends Collection<E> {2 3 // 集合信息4 int size(); // 获取队列大小5 boolean isEmpty(); // 判断队列是否为空6 boolean contains(Object o); // 判断是否包含指定元素7 void clear(); // 清空队列8 9 // 迭代器10 Iterator<E> iterator(); // 获取迭代器11}| 方法 | 描述 | 特点 | 适用场景 |
|---|---|---|---|
size() | 获取队列中元素数量 | O(1)时间复杂度 | 需要知道队列大小时 |
isEmpty() | 检查队列是否为空 | 比size()==0更高效 | 判断队列空状态时 |
contains(Object) | 检查元素是否存在 | O(n)时间复杂度 | 需要确认元素存在时 |
clear() | 清空所有元素 | 不保留容量 | 完全重置队列时 |
iterator() | 获取迭代器 | 可能不保证顺序 | 需要遍历所有元素时 |
使用迭代器遍历Queue时要注意:
- 迭代顺序可能与Queue的出队顺序不一致
- 并发修改会导致ConcurrentModificationException
- 某些Queue实现不支持迭代器的remove()操作
1Queue<String> queue = new ArrayDeque<>();2queue.add("A");3queue.add("B");4queue.add("C");56// 检查大小和内容7System.out.println("Size: " + queue.size()); // 输出38System.out.println("Contains 'B': " + queue.contains("B")); // 输出true910// 使用迭代器遍历11for (String item : queue) {12 System.out.println(item); // 输出A, B, C (顺序可能因实现而异)13}1415// 清空队列16queue.clear();17System.out.println("Is empty: " + queue.isEmpty()); // 输出true1.5 Queue接口方法对比分析
| 操作类型 | 方法名 | 成功时返回值 | 失败时行为 | 使用场景 |
|---|---|---|---|---|
| 添加操作 | add(E) | true | 抛出异常 | 必须成功的场景 |
| 添加操作 | offer(E) | true | 返回false | 可容忍失败的场景 |
| 删除操作 | remove() | 被删除的元素 | 抛出异常 | 必须成功的场景 |
| 删除操作 | poll() | 被删除的元素 | 返回null | 可容忍失败的场景 |
| 查看操作 | element() | 头部元素 | 抛出异常 | 必须成功的场景 |
| 查看操作 | peek() | 头部元素 | 返回null | 可容忍失败的场景 |
- 非阻塞场景:优先使用offer()、poll()、peek()方法
- 阻塞场景:使用BlockingQueue的put()、take()方法
- 异常处理:使用add()、remove()、element()方法时注意异常处理
2. Queue实现类详解
- LinkedList 实现
- PriorityQueue 实现
2.1 LinkedList 概述
LinkedList是Queue接口的重要实现类,基于双向链表实现,具有以下特点:
- 🔄 双向链表:每个节点都有前后指针,支持双向遍历
- 📥 队列操作:实现了Queue接口,支持FIFO操作
- 🔀 双端队列:实现了Deque接口,支持两端操作
- ⚡ 插入删除高效:在任意位置插入删除元素时间复杂度都是O(1)
- ⚠️ 线程不安全:在多线程环境下需要外部同步
适用场景
- 需要队列功能的场景
- 需要双端队列功能的场景
- 频繁的插入删除操作
- 元素数量变化较大的场景
2.2 LinkedList 内部结构
LinkedList基于双向链表实现,每个节点都包含对前一个和后一个节点的引用,支持双向遍历。
核心字段
1public class LinkedList<E> extends AbstractSequentialList<E>2 implements List<E>, Deque<E>, Cloneable, java.io.Serializable {3 4 // 双向链表节点5 private static class Node<E> {6 E item; // 存储的元素7 Node<E> next; // 下一个节点8 Node<E> prev; // 上一个节点9 10 Node(Node<E> prev, E element, Node<E> next) {11 this.item = element;12 this.next = next;13 this.prev = prev;14 }15 }16 17 // 头节点(第一个元素)18 transient Node<E> first;19 20 // 尾节点(最后一个元素)21 transient Node<E> last;22 23 // 链表中的元素个数24 transient int size = 0;25}内存布局示意图
1LinkedList 实例2┌─────────────────────────────────────────┐3│ first: Node<E> │4│ last: Node<E> │5│ size: 3 │6│ modCount: 1 │7└─────────────────────────────────────────┘8 │ │9 ▼ ▼10 ┌─────────┐ ┌─────────┐ ┌─────────┐11 │ Node 1 │◄──►│ Node 2 │◄──►│ Node 3 │12 │ item: A │ │ item: B │ │ item: C │13 │ prev: │ │ prev: 1 │ │ prev: 2 │14 │ next: 2 │ │ next: 3 │ │ next: │15 └─────────┘ └─────────┘ └─────────┘2.3 LinkedList 队列操作实现
2.3.1 添加元素
1public class LinkedList<E> extends AbstractSequentialList<E> {2 3 /**4 * 在链表头部添加元素5 * 时间复杂度:O(1)6 */7 private void linkFirst(E e) {8 final Node<E> f = first;9 final Node<E> newNode = new Node<>(null, e, f);10 first = newNode;11 if (f == null) {12 last = newNode; // 如果链表为空13 } else {14 f.prev = newNode; // 设置原头节点的前驱15 }16 size++;17 modCount++;18 }19 20 /**21 * 在链表尾部添加元素22 * 时间复杂度:O(1)23 */24 void linkLast(E e) {25 final Node<E> l = last;26 final Node<E> newNode = new Node<>(l, e, null);27 last = newNode;28 if (l == null) {29 first = newNode; // 如果链表为空30 } else {31 l.next = newNode; // 设置原尾节点的后继32 }33 size++;34 modCount++;35 }36}2.3.2 删除元素
1public class LinkedList<E> extends AbstractSequentialList<E> {2 3 /**4 * 删除头节点5 * 时间复杂度:O(1)6 */7 private E unlinkFirst(Node<E> f) {8 final E element = f.item;9 final Node<E> next = f.next;10 f.item = null;11 f.next = null; // 帮助GC12 first = next;13 if (next == null) {14 last = null; // 如果链表变为空15 } else {16 next.prev = null; // 新头节点的前驱设为null17 }18 size--;19 modCount++;20 return element;21 }22 23 /**24 * 删除尾节点25 * 时间复杂度:O(1)26 */27 private E unlinkLast(Node<E> l) {28 final E element = l.item;29 final Node<E> prev = l.prev;30 l.item = null;31 l.prev = null; // 帮助GC32 last = prev;33 if (prev == null) {34 first = null; // 如果链表变为空35 } else {36 prev.next = null; // 新尾节点的后继设为null37 }38 size--;39 modCount++;40 return element;41 }42}2.4 LinkedList 性能分析
2.4.1 时间复杂度对比
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 头部插入 | O(1) | 直接修改头节点引用 |
| 尾部插入 | O(1) | 直接修改尾节点引用 |
| 头部删除 | O(1) | 直接修改头节点引用 |
| 尾部删除 | O(1) | 直接修改尾节点引用 |
| 随机访问 | O(n) | 需要遍历链表 |
| 查找元素 | O(n) | 需要遍历比较 |
2.4.2 空间复杂度
- 节点开销:每个元素需要额外的节点对象(包含item、next、prev引用)
- 内存分散:元素在内存中分散存储,缓存不友好
- 引用开销:每个节点有两个引用(next、prev)
2.5 LinkedList 使用示例
2.5.1 基本队列操作示例
1public class LinkedListQueueExample {2 3 public static void main(String[] args) {4 // 创建LinkedList作为Queue使用5 Queue<String> queue = new LinkedList<>();6 7 // 添加元素8 queue.offer("Java");9 queue.offer("Python");10 queue.offer("C++");11 queue.offer("JavaScript");12 13 System.out.println("队列大小: " + queue.size());14 System.out.println("队列是否为空: " + queue.isEmpty());15 16 // 查看头部元素17 String head = queue.peek();18 System.out.println("头部元素: " + head);19 20 // 删除并返回头部元素21 String removed = queue.poll();22 System.out.println("删除的元素: " + removed);23 24 // 遍历队列25 System.out.println("剩余元素:");26 while (!queue.isEmpty()) {27 System.out.println(queue.poll());28 }29 }30}2.5.2 双端队列操作示例
1public class LinkedListDequeExample {2 3 public static void main(String[] args) {4 // 创建LinkedList作为Deque使用5 Deque<String> deque = new LinkedList<>();6 7 // 头部操作8 deque.addFirst("First");9 deque.offerFirst("OfferFirst");10 11 // 尾部操作12 deque.addLast("Last");13 deque.offerLast("OfferLast");14 15 System.out.println("双端队列: " + deque);16 17 // 头部查看和删除18 String first = deque.getFirst();19 System.out.println("第一个元素: " + first);20 21 String removedFirst = deque.removeFirst();22 System.out.println("删除的第一个元素: " + removedFirst);23 24 // 尾部查看和删除25 String last = deque.getLast();26 System.out.println("最后一个元素: " + last);27 28 String removedLast = deque.removeLast();29 System.out.println("删除的最后一个元素: " + removedLast);30 31 System.out.println("操作后的双端队列: " + deque);32 }33}2.5.3 栈操作示例
1public class LinkedListStackExample {2 3 public static void main(String[] args) {4 // 使用LinkedList实现栈5 Deque<String> stack = new LinkedList<>();6 7 // 压栈操作8 stack.push("元素1");9 stack.push("元素2");10 stack.push("元素3");11 12 System.out.println("栈大小: " + stack.size());13 14 // 弹栈操作15 while (!stack.isEmpty()) {16 String element = stack.pop();17 System.out.println("弹栈: " + element);18 }19 20 // 使用LinkedList实现队列21 Queue<String> queue = new LinkedList<>();22 23 // 入队操作24 queue.offer("任务1");25 queue.offer("任务2");26 queue.offer("任务3");27 28 // 出队操作29 while (!queue.isEmpty()) {30 String task = queue.poll();31 System.out.println("处理任务: " + task);32 }33 }34}3536</TabItem>37<TabItem value="arraydeque" label="ArrayDeque 实现">3839### 3.1 ArrayDeque 概述4041:::tip[核心特点]42ArrayDeque是基于数组实现的双端队列,是Java中最高效的队列实现之一,具有以下特点:43- 📊 **数组实现**:基于循环数组实现,存取效率高44- 🔄 **双端操作**:支持在两端高效添加和删除元素45- ⚡ **无容量限制**:自动扩容,无需指定初始大小46- ❌ **不允许null**:不能存储null元素47- 📈 **高性能**:在大多数场景下性能优于LinkedList48:::4950#### 适用场景51- 需要在两端高效操作的队列场景52- 实现栈或队列的场景53- 需要频繁添加、删除元素的场景54- 对内存占用敏感的场景5556### 3.2 ArrayDeque 内部结构5758```java title="ArrayDeque核心字段"59public class ArrayDeque<E> extends AbstractCollection<E>60 implements Deque<E>, Cloneable, Serializable {61 62 // 存储元素的数组63 transient Object[] elements;64 65 // 头部元素的索引66 transient int head;67 68 // 尾部元素的下一个位置的索引69 transient int tail;70 71 // 最小初始容量72 private static final int MIN_INITIAL_CAPACITY = 8;73}内存布局示意图
1ArrayDeque 实例 (循环数组)2┌─────────────────────────────────────────┐3│ elements: Object[] │4│ ├── [0] → D │5│ ├── [1] → E │6│ ├── [2] → null │7│ ├── [3] → null │8│ ├── [4] → null │9│ ├── [5] → null │10│ ├── [6] → A │11│ ├── [7] → B │12│ └── [8] → C │13│ head: 6 │14│ tail: 2 │15└─────────────────────────────────────────┘1617逻辑队列: [A, B, C, D, E] (head指向A,tail指向D后面的位置)4.1 PriorityQueue 概述
PriorityQueue是基于优先级堆的无界队列,具有以下特点:
- 🏆 优先级堆:基于数组实现的二叉堆数据结构
- 🔄 自动排序:元素按照自然顺序或自定义比较器排序
- 📈 无界队列:容量可以动态增长
- ❌ 不允许null:不支持null元素
- ⚠️ 线程不安全:在多线程环境下需要外部同步
适用场景
- 需要按优先级处理任务的场景
- 任务调度系统
- 事件处理系统
- 需要自动排序的数据处理
3.2 PriorityQueue 内部结构
PriorityQueue基于数组实现的二叉堆,通过堆的性质保证优先级顺序。
核心字段
1public class PriorityQueue<E> extends AbstractQueue<E>2 implements java.io.Serializable {3 4 // 默认初始容量5 private static final int DEFAULT_INITIAL_CAPACITY = 11;6 7 // 存储元素的数组8 transient Object[] queue;9 10 // 元素个数11 private int size = 0;12 13 // 比较器14 private final Comparator<? super E> comparator;15 16 // 修改次数17 transient int modCount = 0;18}构造方法
1public class PriorityQueue<E> extends AbstractQueue<E> {2 3 /**4 * 构造一个默认容量的优先级队列5 */6 public PriorityQueue() {7 this(DEFAULT_INITIAL_CAPACITY, null);8 }9 10 /**11 * 构造一个指定初始容量的优先级队列12 */13 public PriorityQueue(int initialCapacity) {14 this(initialCapacity, null);15 }16 17 /**18 * 构造一个指定比较器的优先级队列19 */20 public PriorityQueue(Comparator<? super E> comparator) {21 this(DEFAULT_INITIAL_CAPACITY, comparator);22 }23 24 /**25 * 构造一个指定初始容量和比较器的优先级队列26 */27 public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) {28 if (initialCapacity < 1)29 throw new IllegalArgumentException();30 this.queue = new Object[initialCapacity];31 this.comparator = comparator;32 }33}内存布局示意图
1PriorityQueue 实例 (最小堆)2┌─────────────────────────────────────────┐3│ queue: Object[] │4│ ├── [0] → 1 (根节点) │5│ ├── [1] → 3 (左子节点) │6│ ├── [2] → 5 (右子节点) │7│ ├── [3] → 7 │8│ ├── [4] → 9 │9│ └── [5] → null │10│ size: 5 │11│ comparator: null (自然顺序) │12└─────────────────────────────────────────┘1314堆的树形结构:15 116 / \17 3 518 / \19 7 93.3 PriorityQueue 核心方法实现
3.3.1 添加元素
1public class PriorityQueue<E> extends AbstractQueue<E> {2 3 /**4 * 添加元素到优先级队列5 * 时间复杂度:O(log n)6 */7 public boolean offer(E e) {8 if (e == null)9 throw new NullPointerException();10 modCount++;11 int i = size;12 if (i >= queue.length)13 grow(i + 1); // 扩容14 size = i + 1;15 if (i == 0)16 queue[0] = e; // 第一个元素17 else18 siftUp(i, e); // 上浮操作19 return true;20 }21 22 /**23 * 上浮操作(最小堆)24 * 时间复杂度:O(log n)25 */26 private void siftUp(int k, E x) {27 if (comparator != null)28 siftUpUsingComparator(k, x);29 else30 siftUpComparable(k, x);31 }32 33 /**34 * 使用自然顺序的上浮操作35 */36 @SuppressWarnings("unchecked")37 private void siftUpComparable(int k, E x) {38 Comparable<? super E> key = (Comparable<? super E>) x;39 while (k > 0) {40 int parent = (k - 1) >>> 1; // 父节点索引41 Object e = queue[parent];42 if (key.compareTo((E) e) >= 0)43 break;44 queue[k] = e;45 k = parent;46 }47 queue[k] = key;48 }49}3.3.2 删除元素
1public class PriorityQueue<E> extends AbstractQueue<E> {2 3 /**4 * 删除并返回优先级最高的元素5 * 时间复杂度:O(log n)6 */7 public E poll() {8 if (size == 0)9 return null;10 int s = --size;11 modCount++;12 E result = (E) queue[0]; // 根节点13 E x = (E) queue[s]; // 最后一个元素14 queue[s] = null;15 if (s != 0)16 siftDown(0, x); // 下沉操作17 return result;18 }19 20 /**21 * 下沉操作(最小堆)22 * 时间复杂度:O(log n)23 */24 private void siftDown(int k, E x) {25 if (comparator != null)26 siftDownUsingComparator(k, x);27 else28 siftDownComparable(k, x);29 }30 31 /**32 * 使用自然顺序的下沉操作33 */34 @SuppressWarnings("unchecked")35 private void siftDownComparable(int k, E x) {36 Comparable<? super E> key = (Comparable<? super E>) x;37 int half = size >>> 1; // 非叶子节点数量38 while (k < half) {39 int child = (k << 1) + 1; // 左子节点40 Object c = queue[child];41 int right = child + 1; // 右子节点42 if (right < size &&43 ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)44 c = queue[child = right];45 if (key.compareTo((E) c) <= 0)46 break;47 queue[k] = c;48 k = child;49 }50 queue[k] = key;51 }52}3.3.3 扩容机制
1public class PriorityQueue<E> extends AbstractQueue<E> {2 3 /**4 * 扩容方法5 */6 private void grow(int minCapacity) {7 int oldCapacity = queue.length;8 // 新容量 = 旧容量 + 旧容量/2(1.5倍增长)9 int newCapacity = oldCapacity + ((oldCapacity < 64) ?10 (oldCapacity + 2) : (oldCapacity >> 1));11 12 if (newCapacity - MAX_ARRAY_SIZE > 0)13 newCapacity = hugeCapacity(minCapacity);14 15 queue = Arrays.copyOf(queue, newCapacity);16 }17 18 /**19 * 计算最大容量20 */21 private static int hugeCapacity(int minCapacity) {22 if (minCapacity < 0)23 throw new OutOfMemoryError();24 return (minCapacity > MAX_ARRAY_SIZE) ?25 Integer.MAX_VALUE : MAX_ARRAY_SIZE;26 }27}3.4 PriorityQueue 性能分析
3.4.1 时间复杂度对比
| 操作 | 时间复杂度 | 说明 |
|---|---|---|
| 插入元素 | O(log n) | 需要上浮操作调整堆结构 |
| 删除元素 | O(log n) | 需要下沉操作调整堆结构 |
| 查看元素 | O(1) | 直接访问根节点 |
| 查找元素 | O(n) | 需要遍历整个堆 |
3.4.2 空间复杂度
- 存储开销:每个元素占用一个数组位置
- 扩容开销:扩容时会创建新数组,临时占用双倍内存
- 比较器开销:如果使用自定义比较器,需要额外的对象引用
3.5 PriorityQueue 使用示例
3.5.1 基本使用示例
1public class PriorityQueueBasicExample {2 3 public static void main(String[] args) {4 // 自然顺序优先级队列(最小堆)5 Queue<Integer> naturalOrderQueue = new PriorityQueue<>();6 naturalOrderQueue.offer(5);7 naturalOrderQueue.offer(2);8 naturalOrderQueue.offer(8);9 naturalOrderQueue.offer(1);10 naturalOrderQueue.offer(3);11 12 System.out.println("自然顺序优先级队列(从小到大):");13 while (!naturalOrderQueue.isEmpty()) {14 System.out.println(naturalOrderQueue.poll());15 }16 17 // 自定义比较器优先级队列(最大堆)18 Queue<Integer> customOrderQueue = new PriorityQueue<>((a, b) -> b - a);19 customOrderQueue.offer(5);20 customOrderQueue.offer(2);21 customOrderQueue.offer(8);22 customOrderQueue.offer(1);23 customOrderQueue.offer(3);24 25 System.out.println("自定义顺序优先级队列(从大到小):");26 while (!customOrderQueue.isEmpty()) {27 System.out.println(customOrderQueue.poll());28 }29 30 // 字符串优先级队列31 Queue<String> stringQueue = new PriorityQueue<>();32 stringQueue.offer("Java");33 stringQueue.offer("Python");34 stringQueue.offer("C++");35 stringQueue.offer("JavaScript");36 stringQueue.offer("Go");37 38 System.out.println("字符串优先级队列(按字母顺序):");39 while (!stringQueue.isEmpty()) {40 System.out.println(stringQueue.poll());41 }42 }43}3.5.2 自定义对象优先级队列
1public class PriorityQueueCustomExample {2 3 // 任务类4 static class Task implements Comparable<Task> {5 private String name;6 private int priority; // 优先级,数字越小优先级越高7 private long createTime;8 9 public Task(String name, int priority) {10 this.name = name;11 this.priority = priority;12 this.createTime = System.currentTimeMillis();13 }14 15 @Override16 public int compareTo(Task other) {17 // 首先按优先级比较18 int priorityCompare = Integer.compare(this.priority, other.priority);19 if (priorityCompare != 0) {20 return priorityCompare;21 }22 // 优先级相同时,按创建时间比较(先创建的优先)23 return Long.compare(this.createTime, other.createTime);24 }25 26 @Override27 public String toString() {28 return "Task{name='" + name + "', priority=" + priority + "}";29 }30 }31 32 public static void main(String[] args) {33 // 创建任务优先级队列34 Queue<Task> taskQueue = new PriorityQueue<>();35 36 // 添加任务37 taskQueue.offer(new Task("低优先级任务1", 3));38 taskQueue.offer(new Task("高优先级任务1", 1));39 taskQueue.offer(new Task("中优先级任务1", 2));40 taskQueue.offer(new Task("高优先级任务2", 1));41 taskQueue.offer(new Task("低优先级任务2", 3));42 43 System.out.println("任务处理顺序:");44 while (!taskQueue.isEmpty()) {45 Task task = taskQueue.poll();46 System.out.println("处理任务: " + task);47 }48 }49}3.5.3 使用自定义比较器
1public class PriorityQueueComparatorExample {2 3 public static void main(String[] args) {4 // 使用自定义比较器创建优先级队列5 Queue<String> lengthQueue = new PriorityQueue<>((s1, s2) -> {6 // 按字符串长度排序,长度短的优先7 int lengthCompare = Integer.compare(s1.length(), s2.length());8 if (lengthCompare != 0) {9 return lengthCompare;10 }11 // 长度相同时,按字母顺序排序12 return s1.compareTo(s2);13 });14 15 lengthQueue.offer("Java");16 lengthQueue.offer("Python");17 lengthQueue.offer("C++");18 lengthQueue.offer("JavaScript");19 lengthQueue.offer("Go");20 lengthQueue.offer("Rust");21 22 System.out.println("按长度排序的字符串队列:");23 while (!lengthQueue.isEmpty()) {24 System.out.println(lengthQueue.poll());25 }26 27 // 使用Lambda表达式创建比较器28 Queue<Integer> evenFirstQueue = new PriorityQueue<>((a, b) -> {29 boolean aEven = a % 2 == 0;30 boolean bEven = b % 2 == 0;31 32 if (aEven && !bEven) return -1; // a是偶数,b是奇数,a优先33 if (!aEven && bEven) return 1; // a是奇数,b是偶数,b优先34 return Integer.compare(a, b); // 都是偶数或都是奇数,按数值大小35 });36 37 evenFirstQueue.offer(5);38 evenFirstQueue.offer(2);39 evenFirstQueue.offer(8);40 evenFirstQueue.offer(1);41 evenFirstQueue.offer(6);42 evenFirstQueue.offer(3);43 44 System.out.println("偶数优先的整数队列:");45 while (!evenFirstQueue.isEmpty()) {46 System.out.println(evenFirstQueue.poll());47 }48 }49} 5051</TabItem>52<TabItem value="blockingqueue" label="BlockingQueue 实现">5354### 5.1 BlockingQueue 接口概述5556:::tip[核心特点]57BlockingQueue是Queue的子接口,提供了阻塞操作,具有以下特点:58- ⏱️ **阻塞操作**:支持put/take等阻塞方法59- 🔒 **线程安全**:所有操作都是线程安全的60- 📊 **有界队列**:某些实现类是有界的61- 🔄 **生产者-消费者**:天然适合生产者-消费者模式62- ⏳ **超时操作**:支持带超时的阻塞操作63:::6465#### 适用场景66- 生产者-消费者模式67- 线程池任务队列68- 消息队列实现69- 并发数据处理70- 异步任务调度7172### 4.2 BlockingQueue 接口方法7374```java title="BlockingQueue接口方法"75public interface BlockingQueue<E> extends Queue<E> {76 77 // ========== 阻塞添加操作 ==========78 void put(E e) throws InterruptedException; // 阻塞添加79 boolean offer(E e, long timeout, TimeUnit unit) // 超时添加80 throws InterruptedException;81 82 // ========== 阻塞删除操作 ==========83 E take() throws InterruptedException; // 阻塞删除84 E poll(long timeout, TimeUnit unit) // 超时删除85 throws InterruptedException;86 87 // ========== 批量操作 ==========88 int drainTo(Collection<? super E> c); // 批量转移89 int drainTo(Collection<? super E> c, int maxElements); // 限制批量转移90}4.3 ArrayBlockingQueue 实现类详解
4.3.1 ArrayBlockingQueue 概述
ArrayBlockingQueue是基于数组的有界阻塞队列,具有以下特点:
- 数组实现:基于数组的循环队列
- 有界队列:容量固定,创建时指定
- 线程安全:使用ReentrantLock保证线程安全
- FIFO顺序:严格按先进先出顺序处理
- 阻塞操作:支持put/take等阻塞方法
4.3.2 ArrayBlockingQueue 内部结构
1public class ArrayBlockingQueue<E> extends AbstractQueue<E>2 implements BlockingQueue<E>, java.io.Serializable {3 4 // 存储元素的数组5 final Object[] items;6 7 // 下一个要取元素的位置8 int takeIndex;9 10 // 下一个要放元素的位置11 int putIndex;12 13 // 元素个数14 int count;15 16 // 锁17 final ReentrantLock lock;18 19 // 非空条件20 private final Condition notEmpty;21 22 // 非满条件23 private final Condition notFull;24}4.3.3 内存布局示意图
1ArrayBlockingQueue 实例2┌─────────────────────────────────────────┐3│ items: Object[] │4│ ├── [0] → "Java" │5│ ├── [1] → "Python" │6│ ├── [2] → "C++" │7│ ├── [3] → null │8│ └── [4] → null │9│ takeIndex: 0 │10│ putIndex: 3 │11│ count: 3 │12│ lock: ReentrantLock │13│ notEmpty: Condition │14│ notFull: Condition │15└─────────────────────────────────────────┘4.3.4 ArrayBlockingQueue 核心方法实现
1public class ArrayBlockingQueue<E> extends AbstractQueue<E> {2 3 /**4 * 阻塞添加元素5 * 如果队列满,则阻塞等待6 */7 public void put(E e) throws InterruptedException {8 checkNotNull(e);9 final ReentrantLock lock = this.lock;10 lock.lockInterruptibly();11 try {12 while (count == items.length)13 notFull.await(); // 队列满时等待14 enqueue(e);15 } finally {16 lock.unlock();17 }18 }19 20 /**21 * 阻塞删除元素22 * 如果队列空,则阻塞等待23 */24 public E take() throws InterruptedException {25 final ReentrantLock lock = this.lock;26 lock.lockInterruptibly();27 try {28 while (count == 0)29 notEmpty.await(); // 队列空时等待30 return dequeue();31 } finally {32 lock.unlock();33 }34 }35 36 /**37 * 入队操作38 */39 private void enqueue(E x) {40 final Object[] items = this.items;41 items[putIndex] = x;42 if (++putIndex == items.length)43 putIndex = 0; // 循环队列44 count++;45 notEmpty.signal(); // 唤醒等待的消费者46 }47 48 /**49 * 出队操作50 */51 private E dequeue() {52 final Object[] items = this.items;53 @SuppressWarnings("unchecked")54 E x = (E) items[takeIndex];55 items[takeIndex] = null;56 if (++takeIndex == items.length)57 takeIndex = 0; // 循环队列58 count--;59 notFull.signal(); // 唤醒等待的生产者60 return x;61 }62}4.3.5 ArrayBlockingQueue 使用示例
1public class ArrayBlockingQueueExample {2 3 public static void main(String[] args) {4 // 创建有界阻塞队列5 BlockingQueue<String> queue = new ArrayBlockingQueue<>(3);6 7 // 生产者线程8 Thread producer = new Thread(() -> {9 try {10 String[] items = {"Java", "Python", "C++", "JavaScript", "Go"};11 for (String item : items) {12 queue.put(item); // 阻塞添加13 System.out.println("生产者添加: " + item);14 Thread.sleep(500);15 }16 } catch (InterruptedException e) {17 e.printStackTrace();18 }19 });20 21 // 消费者线程22 Thread consumer = new Thread(() -> {23 try {24 Thread.sleep(1000); // 延迟消费25 for (int i = 0; i < 5; i++) {26 String item = queue.take(); // 阻塞删除27 System.out.println("消费者处理: " + item);28 Thread.sleep(1000);29 }30 } catch (InterruptedException e) {31 e.printStackTrace();32 }33 });34 35 producer.start();36 consumer.start();37 38 try {39 producer.join();40 consumer.join();41 } catch (InterruptedException e) {42 e.printStackTrace();43 }44 }45}4.4 LinkedBlockingQueue 实现类详解
4.4.1 LinkedBlockingQueue 概述
LinkedBlockingQueue是基于链表的有界或无界阻塞队列,具有以下特点:
- 链表实现:基于单向链表的队列
- 可选有界:可以是有界或无界队列
- 线程安全:使用两个ReentrantLock分别保护头部和尾部
- 高性能:相比ArrayBlockingQueue有更好的并发性能
- 内存效率:动态分配内存,避免内存浪费
4.4.2 LinkedBlockingQueue 内部结构
1public class LinkedBlockingQueue<E> extends AbstractQueue<E>2 implements BlockingQueue<E>, java.io.Serializable {3 4 // 链表节点5 static class Node<E> {6 E item;7 Node<E> next;8 9 Node(E x) { item = x; }10 }11 12 // 队列容量13 private final int capacity;14 15 // 当前元素个数16 private final AtomicInteger count = new AtomicInteger();17 18 // 头节点19 transient Node<E> head;20 21 // 尾节点22 private transient Node<E> last;23 24 // 取锁25 private final ReentrantLock takeLock = new ReentrantLock();26 27 // 非空条件28 private final Condition notEmpty = takeLock.newCondition();29 30 // 放锁31 private final ReentrantLock putLock = new ReentrantLock();32 33 // 非满条件34 private final Condition notFull = putLock.newCondition();35}4.4.3 LinkedBlockingQueue 使用示例
1public class LinkedBlockingQueueExample {2 3 public static void main(String[] args) {4 // 无界队列5 BlockingQueue<String> unboundedQueue = new LinkedBlockingQueue<>();6 7 // 有界队列8 BlockingQueue<String> boundedQueue = new LinkedBlockingQueue<>(100);9 10 // 生产者-消费者模式11 BlockingQueue<String> queue = new LinkedBlockingQueue<>(5);12 13 // 生产者14 Thread producer = new Thread(() -> {15 try {16 for (int i = 0; i < 10; i++) {17 String item = "Item-" + i;18 queue.put(item);19 System.out.println("生产: " + item);20 Thread.sleep(100);21 }22 } catch (InterruptedException e) {23 e.printStackTrace();24 }25 });26 27 // 消费者28 Thread consumer = new Thread(() -> {29 try {30 for (int i = 0; i < 10; i++) {31 String item = queue.take();32 System.out.println("消费: " + item);33 Thread.sleep(200);34 }35 } catch (InterruptedException e) {36 e.printStackTrace();37 }38 });39 40 producer.start();41 consumer.start();42 43 try {44 producer.join();45 consumer.join();46 } catch (InterruptedException e) {47 e.printStackTrace();48 }49 }50}4.5 SynchronousQueue 实现类详解
4.5.1 SynchronousQueue 概述
SynchronousQueue是一个不存储元素的阻塞队列,具有以下特点:
- 不存储元素:每个插入操作必须等待另一个线程的移除操作
- 直接传递:适合线程间直接传递数据
- 性能较高:避免了数据复制和存储开销
- 公平性:支持公平和非公平模式
- 零容量:队列容量始终为0
4.5.2 SynchronousQueue 使用示例
1public class SynchronousQueueExample {2 3 public static void main(String[] args) {4 BlockingQueue<String> queue = new SynchronousQueue<>();5 6 // 生产者线程7 Thread producer = new Thread(() -> {8 try {9 for (int i = 0; i < 5; i++) {10 String item = "Item-" + i;11 queue.put(item);12 System.out.println("生产: " + item);13 }14 } catch (InterruptedException e) {15 e.printStackTrace();16 }17 });18 19 // 消费者线程20 Thread consumer = new Thread(() -> {21 try {22 for (int i = 0; i < 5; i++) {23 String item = queue.take();24 System.out.println("消费: " + item);25 Thread.sleep(100);26 }27 } catch (InterruptedException e) {28 e.printStackTrace();29 }30 });31 32 consumer.start(); // 消费者先启动33 Thread.sleep(100);34 producer.start(); // 生产者后启动35 36 try {37 producer.join();38 consumer.join();39 } catch (InterruptedException e) {40 e.printStackTrace();41 }42 }43}4.6 DelayQueue 实现类详解
4.6.1 DelayQueue 概述
DelayQueue是一个无界阻塞队列,具有以下特点:
- 延迟获取:只有在延迟期满时才能从中提取元素
- 优先级队列:基于PriorityQueue实现
- Delayed接口:元素必须实现Delayed接口
- 定时任务:适合定时任务调度
- 线程安全:所有操作都是线程安全的
4.6.2 DelayQueue 使用示例
1public class DelayQueueExample {2 3 // 延迟元素4 static class DelayedElement implements Delayed {5 private String name;6 private long startTime;7 8 public DelayedElement(String name, long delay) {9 this.name = name;10 this.startTime = System.currentTimeMillis() + delay;11 }12 13 @Override14 public long getDelay(TimeUnit unit) {15 long diff = startTime - System.currentTimeMillis();16 return unit.convert(diff, TimeUnit.MILLISECONDS);17 }18 19 @Override20 public int compareTo(Delayed o) {21 return Long.compare(this.startTime, ((DelayedElement) o).startTime);22 }23 24 @Override25 public String toString() {26 return name + " - " + startTime;27 }28 }29 30 public static void main(String[] args) {31 DelayQueue<DelayedElement> queue = new DelayQueue<>();32 33 // 添加延迟元素34 queue.put(new DelayedElement("Task1", 2000));35 queue.put(new DelayedElement("Task2", 1000));36 queue.put(new DelayedElement("Task3", 3000));37 38 // 消费者线程39 Thread consumer = new Thread(() -> {40 try {41 while (!queue.isEmpty()) {42 DelayedElement element = queue.take();43 System.out.println("执行: " + element);44 }45 } catch (InterruptedException e) {46 e.printStackTrace();47 }48 });49 50 consumer.start();51 52 try {53 consumer.join();54 } catch (InterruptedException e) {55 e.printStackTrace();56 }57 }58}4.7 阻塞队列性能对比
| 实现类 | 底层结构 | 容量 | 线程安全 | 性能特点 | 适用场景 |
|---|---|---|---|---|---|
| ArrayBlockingQueue | 数组 | 有界 | 是 | 中等 | 固定容量场景 |
| LinkedBlockingQueue | 链表 | 可选 | 是 | 高 | 动态容量场景 |
| SynchronousQueue | 无存储 | 0 | 是 | 最高 | 直接传递场景 |
| DelayQueue | 优先级队列 | 无界 | 是 | 中等 | 定时任务场景 |
| PriorityBlockingQueue | 优先级队列 | 无界 | 是 | 中等 | 优先级处理场景 |
5. 实际应用场景
5.1 生产者-消费者模式
1public class ProducerConsumerExample {2 private static final int QUEUE_SIZE = 10;3 private static final BlockingQueue<String> queue = new ArrayBlockingQueue<>(QUEUE_SIZE);4 5 // 生产者6 static class Producer implements Runnable {7 private String name;8 9 public Producer(String name) {10 this.name = name;11 }12 13 @Override14 public void run() {15 try {16 for (int i = 0; i < 5; i++) {17 String item = name + "-Item-" + i;18 queue.put(item);19 System.out.println(name + " 生产: " + item);20 Thread.sleep(100);21 }22 } catch (InterruptedException e) {23 e.printStackTrace();24 }25 }26 }27 28 // 消费者29 static class Consumer implements Runnable {30 private String name;31 32 public Consumer(String name) {33 this.name = name;34 }35 36 @Override37 public void run() {38 try {39 for (int i = 0; i < 5; i++) {40 String item = queue.take();41 System.out.println(name + " 消费: " + item);42 Thread.sleep(200);43 }44 } catch (InterruptedException e) {45 e.printStackTrace();46 }47 }48 }49 50 public static void main(String[] args) {51 Thread producer1 = new Thread(new Producer("Producer1"));52 Thread producer2 = new Thread(new Producer("Producer2"));53 Thread consumer1 = new Thread(new Consumer("Consumer1"));54 Thread consumer2 = new Thread(new Consumer("Consumer2"));55 56 producer1.start();57 producer2.start();58 consumer1.start();59 consumer2.start();60 61 try {62 producer1.join();63 producer2.join();64 consumer1.join();65 consumer2.join();66 } catch (InterruptedException e) {67 e.printStackTrace();68 }69 }70}5.2 任务调度系统
1public class TaskSchedulerExample {2 private static final DelayQueue<DelayedTask> taskQueue = new DelayQueue<>();3 4 static class DelayedTask implements Delayed {5 private String taskName;6 private long executeTime;7 8 public DelayedTask(String taskName, long delay) {9 this.taskName = taskName;10 this.executeTime = System.currentTimeMillis() + delay;11 }12 13 @Override14 public long getDelay(TimeUnit unit) {15 return unit.convert(executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);16 }17 18 @Override19 public int compareTo(Delayed o) {20 return Long.compare(this.executeTime, ((DelayedTask) o).executeTime);21 }22 23 public void execute() {24 System.out.println("执行任务: " + taskName + " 时间: " + new Date());25 }26 }27 28 public static void main(String[] args) {29 // 添加定时任务30 taskQueue.put(new DelayedTask("Task1", 2000));31 taskQueue.put(new DelayedTask("Task2", 1000));32 taskQueue.put(new DelayedTask("Task3", 3000));33 34 // 任务执行器35 Thread executor = new Thread(() -> {36 try {37 while (true) {38 DelayedTask task = taskQueue.take();39 task.execute();40 }41 } catch (InterruptedException e) {42 e.printStackTrace();43 }44 });45 46 executor.start();47 48 try {49 Thread.sleep(5000);50 executor.interrupt();51 } catch (InterruptedException e) {52 e.printStackTrace();53 }54 }55}5.3 消息队列实现
1public class MessageQueueExample {2 3 // 消息类4 static class Message {5 private String id;6 private String content;7 private long timestamp;8 9 public Message(String id, String content) {10 this.id = id;11 this.content = content;12 this.timestamp = System.currentTimeMillis();13 }14 15 @Override16 public String toString() {17 return "Message{id='" + id + "', content='" + content + "', timestamp=" + timestamp + "}";18 }19 }20 21 // 消息队列22 static class MessageQueue {23 private final BlockingQueue<Message> queue;24 private final String name;25 26 public MessageQueue(String name, int capacity) {27 this.name = name;28 this.queue = new LinkedBlockingQueue<>(capacity);29 }30 31 public void sendMessage(Message message) throws InterruptedException {32 queue.put(message);33 System.out.println("发送消息到队列 " + name + ": " + message);34 }35 36 public Message receiveMessage() throws InterruptedException {37 Message message = queue.take();38 System.out.println("从队列 " + name + " 接收消息: " + message);39 return message;40 }41 42 public int size() {43 return queue.size();44 }45 }46 47 public static void main(String[] args) {48 MessageQueue messageQueue = new MessageQueue("main", 100);49 50 // 发送者线程51 Thread sender = new Thread(() -> {52 try {53 for (int i = 0; i < 10; i++) {54 Message message = new Message("msg-" + i, "内容-" + i);55 messageQueue.sendMessage(message);56 Thread.sleep(100);57 }58 } catch (InterruptedException e) {59 e.printStackTrace();60 }61 });62 63 // 接收者线程64 Thread receiver = new Thread(() -> {65 try {66 for (int i = 0; i < 10; i++) {67 messageQueue.receiveMessage();68 Thread.sleep(200);69 }70 } catch (InterruptedException e) {71 e.printStackTrace();72 }73 });74 75 sender.start();76 receiver.start();77 78 try {79 sender.join();80 receiver.join();81 } catch (InterruptedException e) {82 e.printStackTrace();83 }84 }85}5.4 线程池任务队列
1public class ThreadPoolTaskQueueExample {2 3 // 任务类4 static class Task implements Runnable {5 private String name;6 private int priority;7 8 public Task(String name, int priority) {9 this.name = name;10 this.priority = priority;11 }12 13 @Override14 public void run() {15 System.out.println("执行任务: " + name + " (优先级: " + priority + ")");16 try {17 Thread.sleep(100); // 模拟任务执行时间18 } catch (InterruptedException e) {19 e.printStackTrace();20 }21 }22 23 public int getPriority() {24 return priority;25 }26 }27 28 // 优先级任务比较器29 static class TaskComparator implements Comparator<Task> {30 @Override31 public int compare(Task t1, Task t2) {32 return Integer.compare(t1.getPriority(), t2.getPriority());33 }34 }35 36 public static void main(String[] args) {37 // 使用优先级队列的线程池38 BlockingQueue<Runnable> taskQueue = new PriorityBlockingQueue<>(100, new TaskComparator());39 40 // 创建线程池41 ThreadPoolExecutor executor = new ThreadPoolExecutor(42 2, // 核心线程数43 4, // 最大线程数44 60L, // 空闲线程存活时间45 TimeUnit.SECONDS,46 taskQueue47 );48 49 // 提交任务50 executor.submit(new Task("高优先级任务1", 1));51 executor.submit(new Task("低优先级任务1", 3));52 executor.submit(new Task("中优先级任务1", 2));53 executor.submit(new Task("高优先级任务2", 1));54 executor.submit(new Task("低优先级任务2", 3));55 56 // 关闭线程池57 executor.shutdown();58 59 try {60 executor.awaitTermination(5, TimeUnit.SECONDS);61 } catch (InterruptedException e) {62 e.printStackTrace();63 }64 }65}6. 最佳实践总结
6.1 Queue实现类选择策略
选择合适的Queue实现类需要考虑以下因素:
- 使用场景:一般队列、优先级队列、阻塞队列
- 性能要求:插入删除性能、内存使用效率
- 线程安全:单线程环境还是多线程环境
- 容量要求:有界队列还是无界队列
- 功能需求:是否需要阻塞操作、优先级排序等
| 实现类 | 适用场景 | 优势 | 劣势 |
|---|---|---|---|
| LinkedList | 一般队列、双端队列 | 插入删除快、功能丰富 | 随机访问慢、线程不安全 |
| PriorityQueue | 优先级处理 | 自动排序、性能稳定 | 线程不安全、内存开销 |
| ArrayBlockingQueue | 固定容量生产者-消费者 | 线程安全、性能稳定 | 容量固定、内存浪费 |
| LinkedBlockingQueue | 动态容量生产者-消费者 | 线程安全、内存效率高 | 内存分配开销 |
| SynchronousQueue | 线程间直接传递 | 性能最高、零内存开销 | 容量为零、使用复杂 |
| DelayQueue | 定时任务调度 | 延迟处理、自动排序 | 使用复杂、性能中等 |
6.2 性能优化策略
1public class QueuePerformanceOptimization {2 3 /**4 * 预分配容量优化5 */6 public static void capacityOptimization() {7 // 知道大概元素数量时,预分配容量8 int expectedSize = 1000;9 BlockingQueue<String> optimizedQueue = new ArrayBlockingQueue<>(expectedSize);10 11 // 避免频繁扩容12 for (int i = 0; i < expectedSize; i++) {13 try {14 optimizedQueue.put("item" + i);15 } catch (InterruptedException e) {16 e.printStackTrace();17 }18 }19 }20 21 /**22 * 批量操作优化23 */24 public static void batchOperationOptimization() {25 Queue<String> queue = new LinkedList<>();26 Collection<String> items = Arrays.asList("a", "b", "c", "d", "e");27 28 // 批量添加,比循环add效率高29 queue.addAll(items);30 31 // 批量处理32while (!queue.isEmpty()) {33 String item = queue.poll();34 // 处理元素35 }36 }37 38 /**39 * 阻塞操作优化40 */41 public static void blockingOperationOptimization() {42 BlockingQueue<String> queue = new LinkedBlockingQueue<>(10);43 44 // 使用超时操作避免无限阻塞45 try {46 boolean offered = queue.offer("item", 1, TimeUnit.SECONDS);47 if (!offered) {48 System.out.println("添加失败,队列已满");49 }50 51 String item = queue.poll(1, TimeUnit.SECONDS);52 if (item == null) {53 System.out.println("获取失败,队列为空");54 }55 } catch (InterruptedException e) {56 e.printStackTrace();57 }58 }59}6.3 常见陷阱和解决方案
- 无限阻塞:使用put/take方法时可能无限阻塞,应使用超时版本
- 内存泄漏:无界队列可能导致内存溢出,应设置合理的容量限制
- 线程安全问题:LinkedList和PriorityQueue不是线程安全的
- 优先级队列排序:自定义对象必须实现Comparable接口或提供Comparator
- 延迟队列使用:元素必须实现Delayed接口
1public class QueueCommonTraps {2 3 /**4 * 无限阻塞陷阱5 */6 public static void infiniteBlockingTrap() {7 BlockingQueue<String> queue = new ArrayBlockingQueue<>(1);8 9 // 错误:可能无限阻塞10 try {11 queue.put("item1");12 queue.put("item2"); // 可能无限阻塞13 } catch (InterruptedException e) {14 e.printStackTrace();15 }16 17 // 正确:使用超时操作18 try {19 boolean offered = queue.offer("item2", 1, TimeUnit.SECONDS);20 if (!offered) {21 System.out.println("添加失败,超时");22 }23 } catch (InterruptedException e) {24 e.printStackTrace();25 }26 }27 28 /**29 * 线程安全问题30 */31 public static void threadSafetyTrap() {32 Queue<String> queue = new LinkedList<>(); // 线程不安全33 34 // 错误:多线程访问35 Thread t1 = new Thread(() -> {36 for (int i = 0; i < 100; i++) {37 queue.offer("item" + i);38 }39 });40 41 Thread t2 = new Thread(() -> {42 for (int i = 0; i < 100; i++) {43 queue.poll();44 }45 });46 47 // 正确:使用线程安全的队列48 BlockingQueue<String> safeQueue = new LinkedBlockingQueue<>();49 // 或者使用Collections.synchronizedQueue(queue)50 }51 52 /**53 * 优先级队列陷阱54 */55 public static void priorityQueueTrap() {56 // 错误:自定义对象没有实现Comparable57 // Queue<CustomObject> queue = new PriorityQueue<>(); // 编译错误58 59 // 正确:实现Comparable接口60 Queue<ComparableObject> queue1 = new PriorityQueue<>();61 62 // 或者提供Comparator63 Queue<CustomObject> queue2 = new PriorityQueue<>((o1, o2) -> o1.name.compareTo(o2.name));64 }65 66 static class ComparableObject implements Comparable<ComparableObject> {67 String name;68 69 @Override70 public int compareTo(ComparableObject other) {71 return this.name.compareTo(other.name);72 }73 }74 75 static class CustomObject {76 String name;77 }78}6.4 测试和调试建议
1public class QueueTestingDebugging {2 3 /**4 * 单元测试示例5 */6 public static void unitTestExample() {7 Queue<String> queue = new LinkedList<>();8 9 // 测试添加操作10 assert queue.isEmpty();11 queue.offer("test");12 assert queue.size() == 1;13 assert "test".equals(queue.peek());14 15 // 测试删除操作16 String removed = queue.poll();17 assert "test".equals(removed);18 assert queue.isEmpty();19 20 System.out.println("所有测试通过");21 }22 23 /**24 * 性能测试示例25 */26 public static void performanceTestExample() {27 int size = 100000;28 29 // LinkedList性能测试30 long start = System.currentTimeMillis();31 Queue<Integer> linkedListQueue = new LinkedList<>();32 for (int i = 0; i < size; i++) {33 linkedListQueue.offer(i);34 }35 long linkedListTime = System.currentTimeMillis() - start;36 37 // PriorityQueue性能测试38 start = System.currentTimeMillis();39 Queue<Integer> priorityQueue = new PriorityQueue<>();40 for (int i = 0; i < size; i++) {41 priorityQueue.offer(i);42 }43 long priorityQueueTime = System.currentTimeMillis() - start;44 45 System.out.println("LinkedList添加" + size + "个元素耗时: " + linkedListTime + "ms");46 System.out.println("PriorityQueue添加" + size + "个元素耗时: " + priorityQueueTime + "ms");47 }48 49 /**50 * 并发测试示例51 */52 public static void concurrentTestExample() {53 BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);54 int producerCount = 3;55 int consumerCount = 2;56 57 // 生产者线程58 for (int i = 0; i < producerCount; i++) {59 final int producerId = i;60 new Thread(() -> {61 try {62 for (int j = 0; j < 100; j++) {63 queue.put("Producer" + producerId + "-Item" + j);64 Thread.sleep(10);65 }66 } catch (InterruptedException e) {67 e.printStackTrace();68 }69 }).start();70 }71 72 // 消费者线程73 for (int i = 0; i < consumerCount; i++) {74 final int consumerId = i;75 new Thread(() -> {76 try {77 for (int j = 0; j < 150; j++) {78 String item = queue.take();79 System.out.println("Consumer" + consumerId + " 处理: " + item);80 Thread.sleep(20);81 }82 } catch (InterruptedException e) {83 e.printStackTrace();84 }85 }).start();86 }87 }88}7. 总结
Queue接口作为Java集合框架的重要组成部分,提供了丰富多样的队列实现,满足了不同场景下的需求。通过深入理解各种Queue实现类的特性和使用场景,我们可以构建出高效、可靠的并发应用程序。
在实际开发中,需要综合考虑以下几个方面:
- 功能需求:根据业务需求选择合适的队列类型
- 性能要求:考虑插入删除性能、内存使用效率
- 并发安全:在多线程环境下选择合适的线程安全实现
- 资源管理:合理设置队列容量,避免内存溢出
- 错误处理:正确处理阻塞操作和异常情况
通过合理使用Queue集合,我们可以构建出高效、可靠的Java应用程序。
8. 面试题精选
8.1 基础概念题
Q: Queue和Stack的区别是什么?
A: 主要区别包括:
- 处理顺序:Queue是先进先出(FIFO),Stack是后进先出(LIFO)
- 继承关系:Queue继承自Collection,Stack继承自Vector
- 阻塞操作:Queue的某些实现类支持阻塞操作,Stack不支持
- 使用场景:Queue适合任务调度,Stack适合函数调用栈
Q: PriorityQueue的工作原理是怎样的?
A: PriorityQueue工作原理:
- 底层结构:基于数组实现的二叉堆
- 排序机制:使用自然顺序或自定义比较器排序
- 插入操作:O(log n),需要上浮操作调整堆结构
- 删除操作:O(log n),需要下沉操作调整堆结构
- 堆性质:保证根节点是优先级最高(或最低)的元素
8.2 性能优化题
Q: 如何优化Queue的性能?
A: 主要优化策略:
- 预分配容量:使用带初始容量的构造函数
- 批量操作:使用addAll()而非循环add()
- 选择合适的实现类:根据使用场景选择最合适的队列
- 避免频繁扩容:合理设置初始容量
- 使用超时操作:避免无限阻塞
Q: 什么场景下选择阻塞队列?
A: 阻塞队列适用场景:
- 生产者-消费者模式
- 线程池任务队列
- 需要线程间协调的场景
- 需要控制并发度的场景
- 异步任务处理
8.3 实践应用题
Q: 如何实现线程安全的Queue操作?
A: 线程安全方案:
- 使用阻塞队列:ArrayBlockingQueue、LinkedBlockingQueue等
- Collections.synchronizedQueue():包装现有Queue
- 外部同步:使用synchronized或Lock
- 并发集合:考虑使用ConcurrentLinkedQueue等
Q: 如何处理Queue中的阻塞操作?
A: 处理方案:
- 使用超时版本:offer(e, timeout, unit)、poll(timeout, unit)
- 异常处理:正确处理InterruptedException
- 中断处理:响应线程中断信号
- 替代方案:使用非阻塞方法配合轮询
Q: PriorityQueue的排序规则是什么?
A: 排序规则:
- 自然顺序:元素必须实现Comparable接口
- 自定义比较器:通过Comparator指定排序规则
- 最小堆:默认情况下,根节点是最小元素
- 最大堆:可以通过比较器实现最大堆
- 稳定性:相同优先级的元素顺序不确定
8.4 高级应用题
Q: 如何实现一个自定义的阻塞队列?
A: 实现步骤:
- 继承AbstractQueue:实现基本的队列操作
- 使用ReentrantLock:保证线程安全
- 使用Condition:实现阻塞和唤醒机制
- 实现put/take方法:提供阻塞操作
- 处理边界条件:队列满/空时的处理
Q: DelayQueue的应用场景有哪些?
A: 应用场景:
- 定时任务调度:延迟执行任务
- 缓存过期:延迟清理过期数据
- 超时处理:延迟处理超时请求
- 重试机制:延迟重试失败的操作
- 事件处理:延迟处理某些事件
Q: SynchronousQueue的特点和用途?
A: 特点和用途:
- 零容量:不存储任何元素
- 直接传递:生产者直接传递给消费者
- 高性能:避免了数据复制和存储开销
- 公平性:支持公平和非公平模式
- 用途:线程间直接数据传递、事件处理
- 理解底层实现:掌握各种Queue的内部结构和工作原理
- 性能分析:能够分析不同操作的时间复杂度
- 场景选择:根据具体需求选择合适的Queue实现
- 并发安全:理解线程安全和并发控制机制
- 最佳实践:了解常见陷阱和优化策略
通过本章的学习,你应该已经掌握了Java Queue集合的核心概念、实现原理和最佳实践。Queue是Java并发编程和异步处理的重要基础,深入理解其特性和使用场景,对于编写高效、可靠的Java程序至关重要。
参与讨论