文章目录
1. Queue接口概述
1.1 核心概念
- Queue 是 Java 集合框架中的核心接口之一,继承自
Collection
接口。 - 核心特性:
- 先进先出(FIFO):默认遵循 FIFO 原则(某些实现如
PriorityQueue
例外)。 - 有序性:元素按插入顺序排列(
PriorityQueue
按优先级排序)。 - 无索引:无法通过索引直接访问元素。
- 允许
null
:部分实现类支持存储null
元素(PriorityQueue
不允许)。
- 先进先出(FIFO):默认遵循 FIFO 原则(某些实现如
1.2 Queue与Collection的区别
特性 | Queue | Collection |
---|---|---|
数据结构 | 无索引、FIFO(或优先级) | 无索引、可重复(List)或唯一(Set) |
存取方式 | 支持队列操作(入队、出队) | 支持迭代器或 toArray() 访问 |
唯一性 | 不强制唯一性(PriorityQueue 可重复) | Set 不允许重复,List 允许重复 |
实现类 | LinkedList 、PriorityQueue 、ArrayDeque 等 | List (如 ArrayList )、Set (如 HashSet ) |
2. Queue的核心方法
2.1 插入元素
boolean add(E e)
添加元素,成功返回true
,若队列已满(有界队列)抛出IllegalStateException
。boolean offer(E e)
添加元素,成功返回true
,若队列已满返回false
(不抛异常)。
2.2 移除元素
E remove()
移除并返回队首元素,若队列为空抛出NoSuchElementException
。E poll()
移除并返回队首元素,若队列为空返回null
。
2.3 查看队首元素
E element()
返回队首元素但不移除,若队列为空抛出NoSuchElementException
。E peek()
返回队首元素但不移除,若队列为空返回null
。
3. Queue的常见实现类
3.1 LinkedList
-
底层结构:双向链表。
-
特点:
- 非线程安全:需外部同步(如
Collections.synchronizedQueue
)。 - 动态大小:支持
addFirst
、addLast
(作为Deque
使用)。 - 适用场景:简单队列或双端队列(
Deque
)。
- 非线程安全:需外部同步(如
-
示例代码:
import java.util.LinkedList; import java.util.Queue; public class LinkedListQueueExample { public static void main(String[] args) { Queue<String> queue = new LinkedList<>(); queue.offer("A"); queue.offer("B"); System.out.println(queue.poll()); // 输出 A System.out.println(queue.peek()); // 输出 B } }
3.2 ArrayDeque
-
底层结构:基于循环数组的双端队列。
-
特点:
- 非线程安全:适合单线程环境。
- 高性能:插入/删除操作复杂度为
O(1)
。 - 不支持
null
:添加null
会抛出NullPointerException
。 - 适用场景:替代
Stack
(LIFO)、高效双端操作。
-
示例代码:
import java.util.ArrayDeque; import java.util.Queue; public class ArrayDequeExample { public static void main(String[] args) { Queue<String> queue = new ArrayDeque<>(); queue.offer("X"); queue.offer("Y"); System.out.println(queue.poll()); // 输出 X System.out.println(queue.peek()); // 输出 Y } }
3.3 PriorityQueue
-
底层结构:基于最小堆的优先级队列。
-
特点:
- 非线程安全:需外部同步。
- 优先级排序:元素按自然顺序或自定义
Comparator
排序。 - 不支持
null
:添加null
会抛出NullPointerException
。 - 适用场景:任务调度、按优先级处理。
-
示例代码:
import java.util.PriorityQueue; import java.util.Queue; public class PriorityQueueExample { public static void main(String[] args) { Queue<Integer> queue = new PriorityQueue<>(); queue.offer(3); queue.offer(1); queue.offer(2); while (!queue.isEmpty()) { System.out.println(queue.poll()); // 输出 1, 2, 3 } } }
3.4 LinkedBlockingQueue
-
底层结构:基于链表的阻塞队列。
-
特点:
- 线程安全:通过锁机制(
ReentrantLock
)实现。 - 可设置容量:有界或无界(默认无界)。
- 适用场景:生产者-消费者模型、多线程任务队列。
- 线程安全:通过锁机制(
-
示例代码:
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.BlockingQueue; public class LinkedBlockingQueueExample { public static void main(String[] args) throws InterruptedException { BlockingQueue<String> queue = new LinkedBlockingQueue<>(2); queue.put("A"); // 阻塞直到有空位 queue.put("B"); System.out.println(queue.take()); // 输出 A System.out.println(queue.take()); // 输出 B } }
3.5 ArrayBlockingQueue
-
底层结构:基于数组的阻塞队列。
-
特点:
- 线程安全:通过锁机制实现。
- 固定容量:初始化时必须指定容量。
- 适用场景:固定容量的阻塞队列(如资源池管理)。
-
示例代码:
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; public class ArrayBlockingQueueExample { public static void main(String[] args) throws InterruptedException { BlockingQueue<String> queue = new ArrayBlockingQueue<>(2); queue.put("X"); // 阻塞直到有空位 queue.put("Y"); System.out.println(queue.take()); // 输出 X System.out.println(queue.take()); // 输出 Y } }
3.6 ConcurrentLinkedQueue
-
底层结构:基于链表的无界非阻塞队列。
-
特点:
- 线程安全:通过 CAS(Compare and Swap)算法实现无锁并发。
- 高性能:适合高并发读写场景。
- 适用场景:高并发环境下的非阻塞队列。
-
示例代码:
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.Queue; public class ConcurrentLinkedQueueExample { public static void main(String[] args) { Queue<String> queue = new ConcurrentLinkedQueue<>(); queue.offer("P"); queue.offer("Q"); System.out.println(queue.poll()); // 输出 P System.out.println(queue.peek()); // 输出 Q } }
3.7 DelayQueue
-
底层结构:基于优先级队列的延迟队列。
-
特点:
- 线程安全:通过锁机制实现。
- 延迟元素:元素只有在指定延迟时间后才能被取出。
- 适用场景:定时任务、缓存过期处理。
-
示例代码:
import java.util.concurrent.DelayQueue; import java.util.concurrent.Delayed; import java.util.concurrent.TimeUnit; class DelayedTask implements Delayed { private final long delayTime; private final long expireTime; public DelayedTask(long delaySeconds) { this.delayTime = delaySeconds; this.expireTime = System.currentTimeMillis() + delaySeconds * 1000; } @Override public long getDelay(TimeUnit unit) { long diff = expireTime - System.currentTimeMillis(); return unit.convert(diff, TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed o) { return Long.compare(this.getDelay(TimeUnit.MILLISECONDS), o.getDelay(TimeUnit.MILLISECONDS)); } @Override public String toString() { return "DelayedTask{" + "delayTime=" + delayTime + '}'; } } public class DelayQueueExample { public static void main(String[] args) throws InterruptedException { DelayQueue<DelayedTask> queue = new DelayQueue<>(); queue.put(new DelayedTask(2)); // 延迟2秒 queue.put(new DelayedTask(1)); // 延迟1秒 while (!queue.isEmpty()) { System.out.println(queue.take()); // 输出 DelayedTask{delayTime=1} 然后 DelayedTask{delayTime=2} } } }
3.8 SynchronousQueue
-
基本概念:
SynchronousQueue 是 Java 并发包(java.util.concurrent)中的一种特殊阻塞队列,其核心特性如下:- 无缓冲设计:不存储任何元素,插入操作(put())必须等待另一个线程的删除操作(take()),反之亦然。
- 直接传递:元素从生产者线程直接传递到消费者线程,无中间缓冲。
- 线程安全:基于锁和条件变量实现线程安全。
- 容量为0:严格来说,其容量为0,无法通过 offer() 方法非阻塞地插入元素。
-
底层结构:
- Java 6 及之前:基于 TransferQueue 实现,使用 Transferer 接口处理元素的直接传递。
- Java 7 及之后:优化为基于 TransferStack 的实现,使用无锁算法(如 CAS)提升性能。
-
特点:
- 无容量:队列不存储任何元素,容量始终为 0。
- 直接传递:插入操作(
put
)必须等待另一个线程的移除操作(take
)完成,反之亦然。 - 严格配对:每个生产者线程的插入操作必须与消费者线程的移除操作一一对应。
-
示例代码:
import java.util.concurrent.SynchronousQueue; public class SynchronousQueueExample { public static void main(String[] args) throws InterruptedException { SynchronousQueue<Integer> queue = new SynchronousQueue<>(); // 生产者线程 Thread producer = new Thread(() -> { try { int data = 1; System.out.println("Producer is putting: " + data); queue.put(data); // 阻塞直到消费者接收数据 System.out.println("Producer put: " + data); } catch (InterruptedException e) { e.printStackTrace(); } }); // 消费者线程 Thread consumer = new Thread(() -> { try { int data = queue.take(); // 阻塞直到生产者放入数据 System.out.println("Consumer received: " + data); } catch (InterruptedException e) { e.printStackTrace(); } }); producer.start(); consumer.start(); } }
4. Deque接口与双端队列
4.1 Deque接口
- 继承关系:
Deque
继承自Queue
,扩展了双端操作方法。 - 核心方法:
addFirst(E e)
/offerFirst(E e)
:在队首添加元素。addLast(E e)
/offerLast(E e)
:在队尾添加元素。removeFirst()
/pollFirst()
:移除并返回队首元素。removeLast()
/pollLast()
:移除并返回队尾元素。
4.2 实现类
-
ArrayDeque
:基于循环数组的双端队列。 -
LinkedList
:基于双向链表的双端队列。 -
示例代码:
import java.util.ArrayDeque; import java.util.Deque; public class DequeExample { public static void main(String[] args) { Deque<String> deque = new ArrayDeque<>(); deque.addFirst("A"); deque.addLast("B"); System.out.println(deque.pollFirst()); // 输出 A System.out.println(deque.pollLast()); // 输出 B } }
5. Queue的线程安全处理
5.1 使用 Collections.synchronizedQueue
- 对
LinkedList
或ArrayDeque
进行同步包装。
Queue<String> syncQueue = Collections.synchronizedQueue(new LinkedList<>());
5.2 使用并发队列
LinkedBlockingQueue
、ArrayBlockingQueue
、ConcurrentLinkedQueue
等实现类。- 适用场景:多线程环境下的生产者-消费者模型、高并发任务队列。
6. Queue的性能对比
操作 | LinkedList | ArrayDeque | PriorityQueue | LinkedBlockingQueue | ConcurrentLinkedQueue |
---|---|---|---|---|---|
添加/删除 | O(1) | O(1) | O(log n) | O(1)(有界) | O(1)(无锁) |
查找 | O(n) | O(1)(两端) | O(1)(队首) | O(1)(队首) | O(1)(队首) |
线程安全 | 否 | 否 | 否 | 是 | 是 |
允许 null | 是 | 否 | 否 | 否 | 是 |
7. Queue的使用注意事项
7.1 选择合适的实现类
- 简单队列:优先选择
LinkedList
或ArrayDeque
。 - 优先级排序:选择
PriorityQueue
。 - 多线程环境:选择
LinkedBlockingQueue
或ConcurrentLinkedQueue
。 - 延迟处理:选择
DelayQueue
。
7.2 自定义对象的排序
- 重写
compareTo
或提供Comparator
:用于PriorityQueue
的优先级排序。
Queue<Person> queue = new PriorityQueue<>((a, b) -> Integer.compare(a.age, b.age));
7.3 性能优化
- 预估容量:初始化时指定合适的容量,减少动态扩容次数。
- 避免频繁中间操作:尽量在尾部进行增删操作。
8. Queue的典型应用场景
场景 | 推荐实现类 | 理由 |
---|---|---|
任务调度 | PriorityQueue | 按优先级处理任务 |
生产者-消费者模型 | LinkedBlockingQueue | 线程安全,支持阻塞操作 |
高并发非阻塞队列 | ConcurrentLinkedQueue | 无锁设计,适合高并发 |
定时任务 | DelayQueue | 元素延迟处理 |
双端操作 | ArrayDeque | 高效的两端插入/删除 |
9. 总结
9.1 Queue的核心优势
- 有序性:严格遵循 FIFO 或优先级排序。
- 灵活性:支持多种实现类(链表、数组、阻塞队列等)。
- 丰富的方法:提供入队、出队、查看队首等全套操作。
9.2 选择实现类的指导原则
- 性能优先:根据操作类型选择
ArrayDeque
、LinkedList
或PriorityQueue
。 - 线程安全:优先使用
LinkedBlockingQueue
、ConcurrentLinkedQueue
。 - 功能需求:根据是否需要优先级、延迟处理选择实现类。