Queue详解

192 阅读5分钟

Java Queue实现类包括LinkedList(双向链表)、PriorityQueue(堆排序)、ArrayDeque(循环数组高效)、LinkedBlockingQueue(线程安全阻塞)及ConcurrentLinkedQueue(CAS高并发),适用于生产者-消费者、任务调度和高频操作场景。

一、Queue 接口核心特性

  • 先进先出(FIFO) :默认行为,但某些实现支持优先级或双端操作。

  • 核心操作

    • 插入add(e)(失败抛异常)、offer(e)(失败返回false)。
    • 移除remove()(失败抛异常)、poll()(失败返回null)。
    • 检查element()(失败抛异常)、peek()(失败返回null)。
  • 扩展功能:双端队列(Deque)支持从两端插入和移除。


二、Queue 主要实现类对比

实现类底层数据结构线程安全顺序特性阻塞支持适用场景
LinkedList双向链表非线程安全FIFO简单队列或双端队列操作
PriorityQueue堆(数组)非线程安全优先级排序任务调度、按优先级处理
ArrayDeque循环数组非线程安全FIFO/LIFO高频插入删除、替代Stack
LinkedBlockingQueue链表线程安全FIFO支持生产者-消费者模型(无界或有界)
ArrayBlockingQueue数组线程安全FIFO支持固定容量阻塞队列
ConcurrentLinkedQueue链表(CAS)线程安全FIFO高并发非阻塞队列
PriorityBlockingQueue堆(数组)线程安全优先级排序支持高并发优先级任务调度

三、各实现类详解

1. LinkedList(作为 Queue 使用)
  • 底层数据结构:双向链表。

    public class LinkedList<E> implements Deque<E> {
        transient Node<E> first; // 头节点
        transient Node<E> last;  // 尾节点
    }
    
  • 操作复杂度

    • 插入/删除头尾元素:O(1)。
    • 随机访问:O(n)。
  • 适用场景:需要简单队列或双端队列,且无需线程安全。


2. PriorityQueue
  • 底层数据结构:基于二叉堆的数组(最小堆或最大堆)。

    // 堆结构示例(数组索引关系):
    parent(i) = (i-1)/2
    leftChild(i) = 2*i + 1
    rightChild(i) = 2*i + 2
    
  • 排序规则

    • 自然排序:元素实现 Comparable 接口。
    • 定制排序:通过 Comparator 指定。
  • 扩容机制

    • 初始容量:默认 11
    • 扩容规则:容量小于64时翻倍,否则增加50%。
  • 操作复杂度:插入(offer)和删除(poll)均为 O(log n)。

  • 适用场景:任务调度(如线程池)、事件优先级处理。


3. ArrayDeque
  • 底层数据结构:循环数组(Object[] elements)。

    public class ArrayDeque<E> implements Deque<E> {
        transient Object[] elements;
        transient int head; // 头指针
        transient int tail; // 尾指针
    }
    
  • 操作机制

    • 插入/删除头尾元素:O(1)。
    • 自动处理数组循环(通过位运算 (head + 1) & (elements.length - 1))。
  • 扩容机制

    • 初始容量:默认 16
    • 触发条件:head == tail(数组已满)。
    • 扩容规则:容量翻倍(保证始终是2的幂次)。
  • 优点:内存紧凑,性能优于 LinkedList

  • 适用场景:高频队列/栈操作(如广度优先搜索)。


4. LinkedBlockingQueue
  • 底层数据结构:单向链表(Node 节点)。

    static class Node<E> {
        E item;
        Node<E> next;
    }
    
  • 线程安全:通过 ReentrantLockCondition 实现。

    public class LinkedBlockingQueue<E> {
        private final ReentrantLock takeLock = new ReentrantLock();
        private final Condition notEmpty = takeLock.newCondition();
        private final ReentrantLock putLock = new ReentrantLock();
        private final Condition notFull = putLock.newCondition();
    }
    
  • 阻塞操作

    • put(e):队列满时阻塞。
    • take():队列空时阻塞。
  • 容量控制

    • 默认无界(Integer.MAX_VALUE),可指定固定容量。
  • 适用场景:生产者-消费者模型(如线程池任务队列)。


5. ArrayBlockingQueue
  • 底层数据结构:固定长度数组。

  • 线程安全:全局 ReentrantLock + 两个 ConditionnotEmptynotFull)。

  • 特点

    • 容量固定,初始化时指定。
    • 公平性可选(通过构造函数指定 fair 参数)。
  • 性能:内存局部性好,但锁竞争较 LinkedBlockingQueue 激烈。

  • 适用场景:需要严格控制资源池大小的场景(如连接池)。


6. ConcurrentLinkedQueue
  • 底层数据结构:基于单向链表的无锁队列(CAS 实现)。

    public class ConcurrentLinkedQueue<E> {
        private transient volatile Node<E> head;
        private transient volatile Node<E> tail;
    }
    
  • 线程安全:通过 sun.misc.Unsafe 的 CAS 操作(如 compareAndSet)。

  • 优点:高并发下性能优异(无锁)。

  • 缺点size() 方法复杂度为 O(n)。

  • 适用场景:高并发非阻塞队列(如实时事件处理)。


7. PriorityBlockingQueue
  • 底层数据结构:基于数组的堆(类似 PriorityQueue)。
  • 线程安全:全局 ReentrantLock + ConditionnotEmpty)。
  • 扩容机制:容量不足时自动扩容(无界队列)。
  • 适用场景:高并发优先级任务调度(如紧急事件处理)。

四、阻塞队列操作对比

方法抛出异常返回特殊值阻塞超时退出
插入add(e)offer(e)put(e)offer(e, time, unit)
移除remove()poll()take()poll(time, unit)
检查element()peek()--

五、数据结构图示(Mermaid)

1. Queue 类图
«interface»Queue+offer()+poll()+peek()«interface»Deque+addFirst()+removeLast()«interface»BlockingQueue+put()+take()LinkedListPriorityQueueLinkedBlockingQueueArrayBlockingQueue
2. ArrayDeque 循环数组
ArrayDeque
5
head=2
tail=5

六、适用场景总结

  • 单线程场景

    • 简单队列ArrayDeque(性能最优)。
    • 优先级处理PriorityQueue
  • 高并发场景

    • 非阻塞队列ConcurrentLinkedQueue
    • 阻塞队列(生产者-消费者)LinkedBlockingQueue(无界)或 ArrayBlockingQueue(固定容量)。
    • 优先级阻塞队列PriorityBlockingQueue
  • 双端操作需求ArrayDequeLinkedList


七、示例代码:生产者-消费者模型

BlockingQueue<Task> queue = new LinkedBlockingQueue<>(10);

// 生产者
Runnable producer = () -> {
    while (true) {
        Task task = generateTask();
        try {
            queue.put(task); // 队列满时阻塞
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
};

// 消费者
Runnable consumer = () -> {
    while (true) {
        try {
            Task task = queue.take(); // 队列空时阻塞
            processTask(task);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
};

// 启动线程
new Thread(producer).start();
new Thread(consumer).start();

八、总结

  • Queue 核心价值:解耦生产者和消费者,平衡处理速度差异。

  • 选择关键

    • 并发需求 → 选择线程安全实现。
    • 顺序要求 → 普通队列 vs 优先级队列。
    • 容量限制 → 无界队列 vs 有界队列。
  • 性能优先:单线程用 ArrayDeque,高并发用 ConcurrentLinkedQueueLinkedBlockingQueue