Java高频面试之集合-02

hello啊,各位观众姥爷们!!!本baby今天来报道了!哈哈哈哈哈嗝

面试官:说说队列queue

Java 队列(Queue)详解

队列(Queue)是 Java 集合框架中一种 先进先出(FIFO) 的线性数据结构,广泛应用于生产者-消费者模型、任务调度、线程池等场景。Java 提供了丰富的队列实现,涵盖线程安全、阻塞、优先级等特性。


一、队列的核心接口与操作

Java 队列的顶层接口是 java.util.Queue,定义以下核心方法:

方法 描述 异常行为 返回值行为
add(E e) 插入元素到队尾,若队列已满抛出异常 抛出 IllegalStateException 成功返回 true
offer(E e) 插入元素到队尾,不抛异常 返回 false(队列满时) 成功返回 true
remove() 移除并返回队头元素,队列为空时抛出异常 抛出 NoSuchElementException 返回移除的元素
poll() 移除并返回队头元素,队列为空时返回 null 返回 null 返回移除的元素或 null
element() 查看队头元素(不移除),队列为空时抛出异常 抛出 NoSuchElementException 返回队头元素
peek() 查看队头元素(不移除),队列为空时返回 null 返回 null 返回队头元素或 null

二、Java 队列的主要实现类

Java 提供了多种队列实现,按功能可分为以下几类:

1. 通用队列
  • LinkedList:基于链表的队列,支持快速插入/删除(非线程安全)。

    Queue<String> queue = new LinkedList<>();
    queue.offer("A");  // 添加元素
    String head = queue.poll();  // 移除并返回队头元素
    
  • PriorityQueue:基于堆的优先级队列,元素按自然顺序或自定义 Comparator 排序。

    Queue<Integer> pq = new PriorityQueue<>();
    pq.offer(5); pq.offer(1); pq.offer(3);
    while (!pq.isEmpty()) {
        System.out.println(pq.poll());  // 输出顺序:1, 3, 5
    }
    
2. 阻塞队列(BlockingQueue)

线程安全的队列,支持阻塞操作(生产者-消费者模型的核心组件):

  • ArrayBlockingQueue:基于数组的有界阻塞队列。

    BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);
    queue.put("Task1");  // 阻塞直到队列有空位
    String task = queue.take();  // 阻塞直到队列非空
    
  • LinkedBlockingQueue:基于链表的阻塞队列,默认无界(可指定容量)。

  • PriorityBlockingQueue:带优先级的无界阻塞队列。

  • SynchronousQueue:不存储元素的队列,直接传递任务(一对一通信)。

3. 并发非阻塞队列
  • ConcurrentLinkedQueue:基于 CAS 的无锁并发队列,高性能但无阻塞语义。
    ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue<>();
    queue.offer("Data");
    String data = queue.poll();
    
4. 双端队列(Deque)

支持在队列两端插入/删除元素:

  • ArrayDeque:基于数组的高效双端队列。
  • `LinkedBlockingDeque``:线程安全的阻塞双端队列。

三、队列的典型应用场景
  1. 任务调度与线程池

    // 线程池使用 LinkedBlockingQueue 作为任务队列
    ExecutorService executor = new ThreadPoolExecutor(
        2, 5, 60, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(100)
    );
    executor.submit(() -> System.out.println("Task executed"));
    
  2. 生产者-消费者模型

    BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();
    // 生产者线程
    new Thread(() -> {
        while (true) {
            int data = generateData();
            queue.put(data);  // 阻塞直到队列有空位
        }
    }).start();
    
    // 消费者线程
    new Thread(() -> {
        while (true) {
            int data = queue.take();  // 阻塞直到队列非空
            processData(data);
        }
    }).start();
    
  3. 广度优先搜索(BFS)

    Queue<Node> queue = new LinkedList<>();
    queue.offer(root);
    while (!queue.isEmpty()) {
        Node node = queue.poll();
        for (Node neighbor : node.neighbors) {
            queue.offer(neighbor);
        }
    }
    

四、阻塞队列的四种处理策略

当队列满或空时,阻塞队列支持以下策略:

策略 对应方法 行为
抛异常 add(e) / remove() 队列满或空时抛出异常
返回特殊值 offer(e) / poll() 队列满返回 false,空返回 null
阻塞等待 put(e) / take() 阻塞直到队列可操作
超时等待 offer(e, timeout) 在指定时间内尝试操作,超时失败

五、队列的选择与性能优化
  1. 线程安全需求

    • 单线程环境:LinkedListArrayDeque
    • 高并发环境:ConcurrentLinkedQueue(非阻塞)或 LinkedBlockingQueue(阻塞)。
  2. 容量限制

    • 内存敏感场景:使用有界队列(如 ArrayBlockingQueue)防止 OOM。
    • 高吞吐场景:无界队列(如 LinkedBlockingQueue)。
  3. 优先级需求

    • 任务按优先级处理:PriorityBlockingQueue
  4. 性能调优

    • 减少锁竞争:使用 ConcurrentLinkedQueue 或 Disruptor(高性能环形队列库)。
    • 避免伪共享:通过填充缓存行优化(如 Disruptor 的 Sequence 设计)。

六、常见问题与注意事项
  1. 线程安全陷阱

    • LinkedListPriorityQueue 非线程安全,多线程环境下需手动同步或使用并发队列。
  2. 阻塞队列的死锁风险

    • 若消费者处理过慢导致队列满,生产者可能永久阻塞。需设置合理的超时或拒绝策略。
  3. 优先级队列的排序一致性

    • 若队列中元素的优先级可能动态变化,需谨慎处理(可能导致堆结构破坏)。

你可能感兴趣的:(java,面试,开发语言)