首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java Queue 與 BlockingQueue 完整指南:核心方法、實作類別與多執行緒應用

     編輯:Java 綜合教學

Java Queue 介面與 Collection 體系

在 Java 集合框架中,Queue 介面與 List、Set 同層級,皆繼承自 Collection 介面。LinkedList 實作了 Queue 介面,當變數型別宣告為 Queue 時,僅能存取 Queue 定義的方法,無法直接呼叫 LinkedList 特有的非 Queue 方法,這屬於介面導向程式設計的封裝特性。此外,BlockingQueue 繼承自 Queue,提供了支援阻塞操作的佇列功能,是多執行緒協作的核心工具。

佇列核心特性:先進先出 (FIFO)

佇列是一種典型的先進先出 (FIFO) 資料結構,具備兩個基本操作:在尾部新增元素、在頭部移除元素。在多執行緒環境下,若嘗試向已滿的阻塞佇列添加元素,或從空的阻塞佇列移除元素,執行緒將會阻塞。這種特性使得阻塞佇列成為生產者-消費者模式的理想選擇:工作者執行緒可將中間結果存入佇列,其他執行緒取出並處理,佇列會自動平衡雙方處理速度的差異,避免資源浪費。

Queue 與 BlockingQueue 方法分類總覽

JDK 1.5 引入的阻塞佇列操作可根據回應方式分為三類:

類別方法行為描述異常/回傳值
拋出異常add(e)新增元素佇列滿拋出 IllegalStateException
remove()移除並回傳頭部元素佇列空拋出 NoSuchElementException
element()檢視頭部元素(不移除)佇列空拋出 NoSuchElementException
回傳特殊值offer(e)新增元素佇列滿回傳 false
poll()移除並回傳頭部元素佇列空回傳 null
peek()檢視頭部元素(不移除)佇列空回傳 null
阻塞等待put(e)新增元素佇列滿時阻塞
take()移除並回傳頭部元素佇列空時阻塞

注意: poll() 與 peek() 在失敗時回傳 null,因此佇列不允許插入 null 值,否則無法區分「佇列為空」與「取出的元素為 null」。

帶超時的操作變體

除了上述方法,BlockingQueue 尚提供帶超時參數的 offer 與 poll:

boolean success = queue.offer(element, 100, TimeUnit.MILLISECONDS);
Object head = queue.poll(100, TimeUnit.MILLISECONDS);

若在指定時間內無法完成操作,則回傳 false 或 null,不會無限期阻塞。

java.util.concurrent 四大阻塞佇列實作詳解

1. LinkedBlockingQueue - 基於鏈表的無界/有界佇列

  • 預設容量: Integer.MAX_VALUE(視為無界),建構時可指定容量轉為有界。
  • 資料結構: 單向鏈表,FIFO 排序。
  • 適用場景: 生產者速度略快於消費者,需緩衝大量任務時。

2. ArrayBlockingQueue - 基於陣列的有界循環佇列

  • 建構時必須指定容量。
  • 公平性選項: 可設定 fair=true,採用 ReentrantLock 公平鎖,等待最久的執行緒優先獲取;但公平性會降低吞吐量,僅在極度需要時啟用。
  • 資料結構: 固定大小陣列,FIFO 原則。

3. PriorityBlockingQueue - 優先級佇列(無界)

  • 排序規則: 依元素自然順序或 Comparator 排序,非 FIFO。
  • 容量: 邏輯上無界(底層以堆實作,類似 PriorityQueue),put 不會阻塞,但記憶體耗盡可能拋出 OutOfMemoryError。
  • 阻塞點: 僅 take() 在佇列空時阻塞。
  • 元素需求: 必須實作 Comparable 或提供 Comparator。

4. DelayQueue - 延遲佇列(無界)

  • 核心機制: 僅能取出「延遲期已滿」的元素,底層以 PriorityQueue 實作。
  • 元素要求: 必須實作 Delayed 介面(含 getDelay(TimeUnit) 與 compareTo)。
  • 行為: 若無元素到期,poll() 回傳 null;不允許 null 元素。
  • 典型用途: 定時任務排程、快取過期清理、網路重試機制。

實戰範例:多執行緒檔案關鍵字搜尋

以下範例示範如何使用 BlockingQueue 協調「檔案掃描執行緒(生產者)」與「內容搜尋執行緒(消費者)」,展現無需額外同步、自動負載平衡的優勢。

import java.io.File;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

public class FileKeywordSearch {
    private static final int SEARCH_THREADS = 4;
    private static final String POISON_PILL = "POISON_PILL";

    public static void main(String[] args) {
        BlockingQueue<File> queue = new LinkedBlockingQueue<>();
        AtomicInteger matchCount = new AtomicInteger(0);
        String keyword = "targetKeyword";
        File rootDir = new File("C:/data");

        // 生產者:遞迴掃描檔案
        Thread producer = new Thread(() -> {
            scanFiles(rootDir, queue);
            // 發送毒丸訊號結束消費者
            for (int i = 0; i < SEARCH_THREADS; i++) {
                try { queue.put(new File(POISON_PILL)); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
            }
        });

        // 消費者:搜尋關鍵字
        Thread[] consumers = new Thread[SEARCH_THREADS];
        for (int i = 0; i < SEARCH_THREADS; i++) {
            consumers[i] = new Thread(() -> {
                while (true) {
                    try {
                        File file = queue.take();
                        if (POISON_PILL.equals(file.getName())) break;
                        if (searchInFile(file, keyword)) {
                            System.out.println("Found in: " + file.getAbsolutePath());
                            matchCount.incrementAndGet();
                        }
                    } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
                }
            });
            consumers[i].start();
        }

        producer.start();
        try { producer.join(); for (Thread t : consumers) t.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        System.out.println("Total matches: " + matchCount.get());
    }

    private static void scanFiles(File dir, BlockingQueue<File> queue) {
        File[] files = dir.listFiles();
        if (files == null) return;
        for (File f : files) {
            if (f.isDirectory()) scanFiles(f, queue);
            else if (f.isFile() && f.getName().endsWith(".txt")) {
                try { queue.put(f); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break; }
            }
        }
    }

    private static boolean searchInFile(File file, String keyword) {
        // 實際讀檔搜尋邏輯略
        return true;
    }
}

總結

  • Queue 介面 定義基本佇列操作,BlockingQueue 增加阻塞語意,解決多執行緒協作難題。
  • 根據業務需求選擇實作:LinkedBlockingQueue(高吞吐緩衝)、ArrayBlockingQueue(固定記憶體、公平性)、PriorityBlockingQueue(優先級處理)、DelayQueue(定時觸發)。
  • 善用 offer/poll/peek 避免異常,或 put/take 實現阻塞同步,搭配超時版本增強健壯性。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved