Java 通過阻塞隊列實現生產者消費者模式

更多 Java 并發編程方面的文章,請參見文集《Java 并發編程》


阻塞隊列 Blocking Queue

  • 當隊列空時,獲取元素的線程會等待
  • 當隊列滿時,存儲元素的線程會等待

提供的方法:

  • 插入元素:

    • add(e):拋出異常
    • offer(e):返回特殊值
    • put(e):一直阻塞
    • offer(e,time,unit):超時退出
  • 移除元素:

    • remove():拋出異常
    • poll():返回特殊值
    • take():一直阻塞
    • poll(time,unit):超時退出

JDK 7 提供了 7 個阻塞隊列

  • ArrayBlockingQueue :一個由數組結構組成的 有界 阻塞隊列。
    • 此隊列按照先進先出(FIFO)的原則對元素進行排序。
    • 默認情況下不保證訪問者公平的訪問隊列,所謂公平訪問隊列是指阻塞的所有生產者線程或消費者線程,當隊列可用時,可以按照阻塞的先后順序訪問隊列,即先阻塞的生產者線程,可以先往隊列里插入元素,先阻塞的消費者線程,可以先從隊列里獲取元素。通常情況下為了保證公平性會降低吞吐量。我們可以使用以下代碼創建一個公平的阻塞隊列:
      ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);
  • LinkedBlockingQueue :一個由鏈表結構組成的 有界 阻塞隊列。
    • 此隊列按照先進先出(FIFO)的原則對元素進行排序。
  • PriorityBlockingQueue :一個支持優先級排序的 無界 阻塞隊列。
    • 默認情況下元素采取自然順序排列,也可以通過比較器 comparator 來指定元素的排序規則。元素按照升序排列。
  • DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
  • SynchronousQueue:一個不存儲元素的阻塞隊列。
  • LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
  • LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。

關于 通過 wait 和 notify 實現生產者消費者模式,可以參考 鏈接
關于 通過 Lock 和 競爭條件 Condition 實現生產者消費者模式,可以參考 鏈接

利用阻塞隊列實現生產者消費者模式,代碼如下:

public class BlockingQueue_Test {
    private static final int MAX_CAPACITY = 10;
    private static ArrayBlockingQueue<Object> goods = new ArrayBlockingQueue<Object>(MAX_CAPACITY);

    public static void main(String[] args) {
        (new ProducerThread()).start();

        (new ConsumerThread()).start();
    }

    static class ProducerThread extends Thread {
        public void run() {
            while (true) {
                // 每隔 1000 毫秒生產一個商品
                try {
                    Thread.sleep(1000);

                    goods.put(new Object());
                    System.out.println("Produce goods, total: " + goods.size());
                } catch (InterruptedException e) {
                }
            }
        }
    }

    static class ConsumerThread extends Thread {
        public void run() {
            while (true) {
                // 每隔 500 毫秒消費一個商品
                try {
                    Thread.sleep(500);

                    goods.take();
                    System.out.println("Consume goods, total: " + goods.size());
                } catch (InterruptedException e) {
                }
            }
        }
    }
}

阻塞隊列的實現原理

ArrayBlockingQueue 為例,實際上使用了 ReentrantLock 和 Condition。

構造方法:

public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}

插入元素,如果隊列已滿,則阻塞 notFull.await();:

public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

移除元素,如果隊列已空,則阻塞 notEmpty.await();:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

引用:
聊聊并發(七)——Java中的阻塞隊列

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 相關文章Java并發編程(一)線程定義、狀態和屬性 Java并發編程(二)同步Java并發編程(三)volatil...
    劉望舒閱讀 5,252評論 1 31
  • 阻塞隊列(BlockingQueue)是一個支持兩個附加操作的隊列。這兩個附加的操作是:在隊列為空時,獲取元素的線...
    端木軒閱讀 1,022評論 0 2
  • 隊列 隊列是先進先出(FIFO)的線性表。在具體應用中通常用鏈表或者數組來實現。隊列只允許在后端(稱為rear)進...
    Showdy閱讀 429評論 0 0
  • 1.阻塞隊列定義阻塞隊列常用于生產者和消費者的場景,生產者是往隊列里添加元素的線程,消費者是從隊列里拿元素的線程。...
    SDY_0656閱讀 449評論 0 1
  • 親愛的大木子: 你好嗎?當你收到這封信的時候應該是2026年了,這是一封穿越時空的信件,不知道你收到這封信的心情會...
    木子不愛糖閱讀 319評論 1 4