Java 队列完整教程:从基础到实战

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}
Java 队列示意图

五、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  {\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 #队列 #并发编程 #数据结构 #编程教程

标签

发表评论