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

Java 多線程系列(六):阻塞隊列簡化生產者-消費者模式

     編輯:Java 綜合教學

Java 多線程系列(六):阻塞隊列簡化生產者-消費者模式

本文通過經典的「生產者-消費者」模型,對比傳統 wait/notify 手動同步與 BlockingQueue 內置同步的實現差異,展示如何利用 JDK 並發包簡化多線程協作代碼。

傳統實現:手動鎖 + Condition

使用 ReentrantLock 配合 Condition 實現緩衝區同步:

public class ConsumerProducer {
    private static Buffer buffer = new Buffer();

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new ProducerTask());
        executor.execute(new ConsumerTask());
        executor.shutdown();
    }

    static class ProducerTask implements Runnable {
        @Override
        public void run() {
            try {
                int i = 1;
                while (true) {
                    System.out.println("Producer writes " + i);
                    buffer.write(i++);
                    Thread.sleep((int)(Math.random() * 10000));
                }
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }

    static class ConsumerTask implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("\t\t\tConsumer reads " + buffer.read());
                    Thread.sleep((int)(Math.random() * 10000));
                }
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }

    static class Buffer {
        private static final int CAPACITY = 1;
        private final LinkedList<Integer> queue = new LinkedList<>();
        private final Lock lock = new ReentrantLock();
        private final Condition notEmpty = lock.newCondition();
        private final Condition notFull = lock.newCondition();

        public void write(int value) {
            lock.lock();
            try {
                while (queue.size() == CAPACITY) {
                    System.out.println("Wait for notFull condition");
                    notFull.await();
                }
                queue.offer(value);
                notEmpty.signal();
            } catch (Exception e) { e.printStackTrace(); }
            finally { lock.unlock(); }
        }

        public int read() {
            int value = 0;
            lock.lock();
            try {
                while (queue.isEmpty()) {
                    System.out.println("\t\t\tWait for notEmpty condition");
                    notEmpty.await();
                }
                value = queue.remove();
                notFull.signal();
            } catch (Exception e) { e.printStackTrace(); }
            finally { lock.unlock(); }
            return value;
        }
    }
}

進階實現:ArrayBlockingQueue 內置同步

ArrayBlockingQueue 是有界阻塞隊列,內部已實現線程安全的 put/take,無需手動編寫同步邏輯:

public class ConsumerProducerUsingBlockingQueue {
    // 容量為 2 的有界隊列
    private static ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(2);

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(2);
        executor.execute(new ProducerTask());
        executor.execute(new ConsumerTask());
        executor.shutdown();
    }

    static class ProducerTask implements Runnable {
        @Override
        public void run() {
            try {
                int i = 1;
                while (true) {
                    System.out.println("Producer writes " + i);
                    buffer.put(i++); // 隊列滿時自動阻塞
                    Thread.sleep((int)(Math.random() * 10000));
                }
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }

    static class ConsumerTask implements Runnable {
        @Override
        public void run() {
            try {
                while (true) {
                    System.out.println("\t\t\tConsumer reads " + buffer.take()); // 隊列空時自動阻塞
                    Thread.sleep((int)(Math.random() * 10000));
                }
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

三大核心阻塞隊列對比

隊列類型 底層結構 特性 適用場景
ArrayBlockingQueue 數組 有界、FIFO、固定容量、支持公平鎖 固定緩衝區、生產消費速率平衡
LinkedBlockingQueue 鏈表 可選有界(默認 Integer.MAX_VALUE)、吞吐量高 高併發任務隊列、線程池工作隊列
PriorityBlockingQueue 堆 無界、按優先級排序、不允許 null 優先級任務調度、定時任務隊列

核心方法速查

操作 拋出異常 返回布爾值 阻塞等待 超時等待
插入 add(e) offer(e) put(e) offer(e, time, unit)
移除 remove() poll() take() poll(time, unit)
檢查 element() peek() — —

最佳實踐總結

  1. 優先使用阻塞隊列:減少手動同步代碼,降低死鎖、活鎖風險。
  2. 合理設置容量:有界隊列防止 OOM,容量過小導致生產者頻繁阻塞,需壓測調優。
  3. 選擇合適類型:高併發用 LinkedBlockingQueue,需優先級用 PriorityBlockingQueue,固定緩衝用 ArrayBlockingQueue。
  4. 避免在鎖中調用阻塞方法:隊列操作已包含同步,外層無需再加鎖。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved