Java 队列完整教程:从基础到实战
Java 队列是并发编程和数据处理的核心工具。掌握队列的原理和使用方法,对于构建高效、稳定的应用系统至关重要。本文将深入讲解 Java 队列的各种实现和最佳实践。
一、Java 队列基础概念
队列(Queue)是一种遵循先进先出(FIFO)原则的线性数据结构。数据从队尾进入,从队头取出。相比栈的后进先出,队列更适合需要按顺序处理任务的场景。
核心特点
- 顺序处理 – 先入先出,保证任务处理的顺序性
- 线程安全 – 部分实现提供并发安全的队列
- 阻塞支持 – 阻塞队列支持等待和超时操作
- 优先级 – 优先队列支持按优先级排序
队列与栈的区别
// 栈(LIFO)- 后进先出\nStack stack = new Stack();\nstack.push(1);\nstack.push(2);\nstack.pop(); // 返回 2\n\n// 队列(FIFO)- 先进先出\nQueue queue = new ArrayDeque();\nqueue.offer(1);\nqueue.offer(2);\nqueue.poll(); // 返回 1\n
操作方式对比
| 操作 | 抛出异常 | 返回特殊值 | 阻塞 |
|---|---|---|---|
| 添加 | add() | offer() | put() |
| 移除 | remove() | poll() | take() |
| 获取 | element() | peek() | – |
二、Queue 接口详解
Queue 接口继承自 Collection 接口,定义了队列的标准操作。
核心方法
// 添加元素\nboolean add(E e); // 超过容量抛异常\nboolean offer(E e); // 超过容量返回 false\n\n// 移除元素\nE remove(); // 队列为空抛异常\nE poll(); // 队列为空返回 null\n\n// 获取元素\nE element(); // 队列为空抛异常\nE peek(); // 队列为空返回 null\n\n// 其他方法\nint size();\nboolean isEmpty();\nvoid clear();\nObject[] toArray();\n
三、ArrayDeque 使用
ArrayDeque 是基于数组的双端队列,性能优于 LinkedList,可作为栈和队列使用。
1. 基本用法
ArrayDeque<String> deque = new ArrayDeque<>();\n\n// 双端操作\ndeque.offerFirst("A");\ndeque.offerLast("B");\nString first = deque.pollFirst(); // "A"\nString last = deque.pollLast(); // "B"\n\n// 作为栈使用\ndeque.push("X");\ndeque.push("Y");\nString top = deque.pop(); // "Y"\n
2. 实际示例
public class ArrayDequeDemo {\n public static void main(String[] args) {\n Deque<String> deque = new ArrayDeque<>();\n \n // 模拟浏览器前进后退\n deque.offerFirst("A");\n deque.offerFirst("B");\n deque.offerFirst("C");\n \n while (!deque.isEmpty()) {\n System.out.println(deque.pollFirst()); // C, B, A\n }\n }\n}
四、LinkedList 使用
LinkedList 是基于双向链表的队列实现,支持在两端高效操作。
1. 基本用法
LinkedList<String> queue = new LinkedList<>();\n\n// 添加元素\nqueue.offer("A");\nqueue.offer("B");\nqueue.offer("C");\n\n// 移除元素\nString item = queue.poll(); // "A"\n\n// 双端操作\nqueue.addFirst("X");\nqueue.addLast("Y");\n
2. 实际示例
public class LinkedListQueueDemo {\n public static void main(String[] args) {\n Queue<Runnable> taskQueue = new LinkedList<>();\n \n // 添加任务\n taskQueue.offer(() -> System.out.println("Task 1"));\n taskQueue.offer(() -> System.out.println("Task 2"));\n \n // 消费任务\n while (!taskQueue.isEmpty()) {\n Runnable task = taskQueue.poll();\n task.run();\n }\n }\n}

五、PriorityQueue 使用
PriorityQueue 是基于堆的优先级队列,元素按优先级出队。
1. 基本用法
// 自然排序(升序)\nPriorityQueue<Integer> pq = new PriorityQueue<>();\npq.offer(5);\npq.offer(2);\npq.offer(8);\nSystem.out.println(pq.poll()); // 2\n\n// 自定义排序(降序)\nPriorityQueue<String> pq = new PriorityQueue<String>((a, b) -> b.compareTo(a));\n
2. 优先级场景
public class Task {\n private String name;\n private int priority;\n \n public Task(String name, int priority) {\n this.name = name;\n this.priority = priority;\n }\n \n public int getPriority() { return priority; }\n public String getName() { return name; }\n}\n\n// 按优先级处理任务\nPriorityQueue<Task> taskQueue = new PriorityQueue<Task>(\n (a, b) -> b.getPriority() - a.getPriority()\n);\n\ntaskQueue.offer(new Task("低优先级", 1));\ntaskQueue.offer(new Task("高优先级", 3));\ntaskQueue.offer(new Task("中优先级", 2));\n\nwhile (!taskQueue.isEmpty()) {\n Task task = taskQueue.poll();\n System.out.println("处理:" + task.getName() + ",优先级:" + task.getPriority());\n}\n
3. 性能说明
插入和删除时间复杂度:O(log n),查询最小值:O(1)
六、阻塞队列(BlockingQueue)
BlockingQueue 是并发编程中的重要组件,支持阻塞式操作,是线程池的基础。
1. 核心实现类
ArrayBlockingQueue – 有界数组队列
// 有界队列\nBlockingQueue<String> queue = new ArrayBlockingQueue<String>(10);\n\nqueue.put("A"); // 队列满则阻塞\nString item = queue.take(); // 队列空则阻塞\n
LinkedBlockingQueue – 有界/无界链表队列
// 指定大小的有界队列\nBlockingQueue<String> queue = new LinkedBlockingQueue<String>(1000);\n\n// 或无界队列(容量 Integer.MAX_VALUE)\nBlockingQueue<String> queue = new LinkedBlockingQueue<String>();\n
PriorityBlockingQueue – 阻塞优先队列
BlockingQueue<Integer> queue = new PriorityBlockingQueue<Integer>();\nqueue.offer(5);\nqueue.offer(2);\nInteger item = queue.take(); // 返回 2\n
SynchronousQueue – 同步队列
// 同步队列,不存储元素\nBlockingQueue<String> queue = new SynchronousQueue<String>();\n\n// put 和 take 必须成对出现\nnew Thread(() -> {\n try {\n queue.put("A");\n } catch (InterruptedException e) {}\n}).start();\n\nqueue.take(); // 等待 put 操作\n
2. 生产者 – 消费者示例
public class ProducerConsumerDemo {\n private static final BlockingQueue<Integer> queue = \n new ArrayBlockingQueue<Integer>(10);\n \n public static void main(String[] args) {\n // 生产者\n Thread producer = new Thread(() -> {\n try {\n for (int i = 0; i < 10; i++) {\n queue.put(i);\n System.out.println("生产:" + i);\n Thread.sleep(100);\n }\n } catch (InterruptedException e) {}\n });\n \n // 消费者\n Thread consumer = new Thread(() -> {\n try {\n while (true) {\n Integer item = queue.take();\n System.out.println("消费:" + item);\n }\n } catch (InterruptedException e) {}\n });\n \n producer.start();\n consumer.start();\n }\n}
3. 阻塞方法对比
| 方法 | 队列为空 | 队列已满 | 说明 |
|---|---|---|---|
| put() | 阻塞 | 阻塞 | 无限等待 |
| offer(E, t, unit) | 返回 true | 阻塞等待 | 指定超时 |
| take() | 阻塞 | 返回 | 取元素 |
| poll(t, unit) | 阻塞等待 | 返回 null | 取元素超时 |
七、实际应用案例
1. 线程池内部使用
// ThreadPoolExecutor 内部使用 LinkedBlockingQueue\nThreadPoolExecutor executor = new ThreadPoolExecutor(\n 10, 20, 60L, TimeUnit.SECONDS,\n new LinkedBlockingQueue<Runnable>(1000)\n);\n
2. 消息队列模拟
public class MessageQueue {\n private BlockingQueue<Message> queue = new LinkedBlockingQueue<Message>();\n \n public void send(Message msg) throws InterruptedException {\n queue.put(msg);\n }\n \n public Message receive() throws InterruptedException {\n return queue.take();\n }\n \n public boolean sendWithTimeout(Message msg, long timeout) \n throws InterruptedException {\n return queue.offer(msg, timeout, TimeUnit.MILLISECONDS);\n }\n}
3. 任务调度系统
public class TaskScheduler {\n private BlockingQueue<Task> taskQueue = \n new PriorityBlockingQueue<Task>((a, b) -> \n a.getDeadline() - b.getDeadline());\n \n public void schedule(Task task) {\n taskQueue.offer(task);\n }\n \n public Task getNextTask() throws InterruptedException {\n return taskQueue.take();\n }\n}
八、最佳实践
1. 选择合适的队列
- 普通队列 – ArrayDeque(高性能)或 LinkedList
- 优先级队列 – PriorityQueue
- 并发队列 – ConcurrentLinkedQueue
- 阻塞队列 – BlockingQueue 实现
2. 线程安全考虑
// 非线程安全\nQueue<String> queue = new ArrayDeque<String>();\n\n// 线程安全 - 外部同步\nsynchronized (queue) {\n queue.add(item);\n}\n\n// 线程安全 - 并发队列\nBlockingQueue<String> queue = new LinkedBlockingQueue<String>();\n
3. 避免常见错误
// ❌ 错误:无界队列可能导致 OOM\nBlockingQueue<Object> queue = new LinkedBlockingQueue<Object>();\n\n// ✅ 正确:使用有界队列\nBlockingQueue<Object> queue = \n new ArrayBlockingQueue<Object>(10000);\n\n// ❌ 错误:忽略 InterruptedException\nqueue.put(item);\n\n// ✅ 正确:处理中断异常\ntry {\n queue.put(item);\n} catch (InterruptedException e) {\n Thread.currentThread().interrupt();\n throw e;\n}\n
九、总结
Java 队列是并发编程和数据处理的核心工具。掌握 Queue 接口、各种实现类及其应用场景,能够帮助开发者构建高效、稳定的并发系统。根据业务需求选择合适的队列类型,是提升系统性能的关键。
无论是普通的 FIFO 队列、优先队列,还是用于线程池的阻塞队列,每一种队列都有其独特的适用场景。理解它们的原理和差异,才能在实际开发中做出正确的选择。
#Java #队列 #并发编程 #数据结构 #编程教程
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
















暂无评论内容