一、Java 隊列體系概覽

Q1:Java 中 Queue 相關的接口和類有哪些?它們的關係是什麼?

  • 核心接口:
  • Queue<E>:基礎 FIFO 隊列(offer/addpoll/removepeek/element
  • Deque<E>:雙端隊列(支持頭尾插入/刪除),繼承 Queue
  • BlockingQueue<E>:阻塞隊列(線程安全),繼承 Queue
  • TransferQueue<E>:可傳遞元素的阻塞隊列(如 LinkedTransferQueue
  • 常用實現類分類:

類型

非阻塞(普通隊列)

阻塞隊列(線程安全)

基於鏈表

LinkedList

LinkedBlockingQueueLinkedTransferQueue

基於數組

ArrayDeque

ArrayBlockingQueue

無存儲結構

SynchronousQueue

優先級隊列

PriorityQueue

PriorityBlockingQueue

注意:

  • LinkedList 實現了 Deque,但不是線程安全
  • ArrayDeque 比 LinkedList 更高效(緩存友好、無節點對象開銷)

二、普通隊列 vs 阻塞隊列

Q2:add/offer、remove/poll、element/peek 有何區別?

方法

拋異常

返回特殊值

阻塞

超時

插入

add(e)

offer(e)

put(e)

offer(e, time, unit)

移除

remove()

poll()

take()

poll(time, unit)

查看

element()

peek()

  • 關鍵區別:
  • add() 在容量滿時拋 IllegalStateException
  • offer() 在容量滿時返回 false(適用於有界隊列)
  • 阻塞隊列 的 put/take 會在隊列滿/空時永久阻塞,直到條件滿足

最佳實踐:
在不確定隊列狀態時,優先使用 offer() / poll() 避免異常。

三、ArrayBlockingQueue vs LinkedBlockingQueue

Q3:ArrayBlockingQueue 和 LinkedBlockingQueue 有什麼區別?如何選型?

特性

ArrayBlockingQueue

LinkedBlockingQueue

底層結構

有界數組(必須指定容量)

無界鏈表(默認 Integer.MAX_VALUE

鎖機制

一把鎖ReentrantLock,生產和消費互斥)

兩把鎖putLock + takeLock,生產消費可並行)

內存佔用

固定(預分配)

動態增長(Node 對象開銷)

性能

小容量時快(緩存友好)

高併發吞吐更高(鎖分離)

風險

安全(有界防 OOM)

默認無界 → 可能 OOM!

  • 線程池為何常用 LinkedBlockingQueue?
    因為其高吞吐特性適合任務緩衝,但生產環境務必設置合理容量

避坑指南:

// 危險!可能 OOM new LinkedBlockingQueue<>(); // 安全做法 new LinkedBlockingQueue<>(1000);

四、SynchronousQueue:零容量隊列

Q4:SynchronousQueue 是什麼?它有什麼特殊用途?

  • 核心特性:
  • 容量為 0,不存儲元素
  • put() 必須等待另一個線程 take(),反之亦然
  • 本質是線程間直接移交(handoff)
  • 應用場景:
  • 線程池的“直接提交”策略Executors.newCachedThreadPool() 使用 SynchronousQueue
  • 高吞吐、低延遲的生產者-消費者模型(無緩衝,即時傳遞)
  • 公平模式:
  • new SynchronousQueue(true):FIFO 順序移交
  • false(默認):棧式 LIFO,性能略高

優勢: 避免任務堆積,快速反饋系統壓力(配合拒絕策略)。