1. 程式人生 > 其它 >JUC併發程式設計快速入門篇(八)—— 阻塞佇列

JUC併發程式設計快速入門篇(八)—— 阻塞佇列

阻塞佇列

BlockingQueue阻塞佇列

阻塞佇列,顧名思義,首先它是一個佇列, 通過一個共享的佇列,可以使得資料由佇列的一端輸入,從另外一端輸出;

  • 當佇列是空的,從佇列中獲取元素的操作將會被阻塞
  • 當佇列是滿的,從佇列中新增元素的操作將會被阻塞
  • 試圖從空的佇列中獲取元素的執行緒將會被阻塞,直到其他執行緒往空的佇列插入新的元素
  • 試圖向已滿的佇列中新增新元素的執行緒將會被阻塞,直到其他執行緒從佇列中移除一個或多個元素或者完全清空,使佇列變得空閒起來並後續新增

常用的佇列主要有以下兩種:

  • 先進先出(FIFO):先插入的佇列的元素也最先出佇列,類似於排隊的功能。從某種程度上來說這種佇列也體現了一種公平性
  • 後進先出(LIFO):後插入佇列的元素最先出佇列,這種佇列優先處理最近發生的事件(棧)

ArrayBlockingQueue(常用)

由陣列結構組成的有界阻塞佇列

BlockingQueue 核心方法

案例

public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        //建立一個長度為3的阻塞佇列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//        LinkedBlockingDeque<String> blockingDeque = new LinkedBlockingDeque<>(3);

        //新增第一組佇列
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        System.out.println(blockingQueue.element());
        //System.out.println(blockingQueue.add("d"));//佇列大小為3,新增第4個元素丟擲異常

        System.out.println(blockingQueue.remove());//a
        System.out.println(blockingQueue.remove());//b
        System.out.println(blockingQueue.remove());//c
//        System.out.println(blockingQueue.remove());//異常

        //新增第二組佇列
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
//        System.out.println(blockingQueue.offer("d"));//佇列大小為3,新增第4個元素丟擲異常

        System.out.println(blockingQueue.poll());//a
        System.out.println(blockingQueue.poll());//b
        System.out.println(blockingQueue.poll());//c
//        System.out.println(blockingQueue.poll());//異常

        //第三組
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
//        blockingQueue.put("d");//不會丟擲異常,自動處於執行緒等待阻塞狀態

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
//        System.out.println(blockingQueue.take());//不會丟擲異常,當執行緒中沒有佇列的時候,無法取佇列,所以處於阻塞狀態

        //第四組
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        //設定阻塞時間,3L為時間長度,TimeUnit.SECONDS為時間單位。3S內未能進佇列,則自動結束
        System.out.println(blockingQueue.offer("d",3L, TimeUnit.SECONDS));
    }
}

LinkedBlockingQueue(常用)

ArrayBlockingQueue 和 LinkedBlockingQueue 是兩個最普通也是最常用的阻塞佇列,一般情況下,在處理多執行緒間的生產者消費者問題,使用這兩個類足以。
由連結串列結構組成的有界(但大小預設值為integer.MAX_VALUE)阻塞佇列
public class BlockingQueueDemo {
    public static void main(String[] args) throws InterruptedException {
        //建立一個長度為3的阻塞佇列
        BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>(3);
//        LinkedBlockingDeque<String> blockingDeque = new LinkedBlockingDeque<>(3);

        //新增第一組佇列
        System.out.println(blockingQueue.add("a"));
        System.out.println(blockingQueue.add("b"));
        System.out.println(blockingQueue.add("c"));
        System.out.println(blockingQueue.element());
        //System.out.println(blockingQueue.add("d"));//佇列大小為3,新增第4個元素丟擲異常

        System.out.println(blockingQueue.remove());//a
        System.out.println(blockingQueue.remove());//b
        System.out.println(blockingQueue.remove());//c
//        System.out.println(blockingQueue.remove());//異常

        //新增第二組佇列
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
//        System.out.println(blockingQueue.offer("d"));//佇列大小為3,新增第4個元素丟擲異常

        System.out.println(blockingQueue.poll());//a
        System.out.println(blockingQueue.poll());//b
        System.out.println(blockingQueue.poll());//c
//        System.out.println(blockingQueue.poll());//異常

        //第三組
        blockingQueue.put("a");
        blockingQueue.put("b");
        blockingQueue.put("c");
//        blockingQueue.put("d");//不會丟擲異常,自動處於執行緒等待阻塞狀態

        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
        System.out.println(blockingQueue.take());
//        System.out.println(blockingQueue.take());//不會丟擲異常,當執行緒中沒有佇列的時候,無法取佇列,所以處於阻塞狀態

        //第四組
        System.out.println(blockingQueue.offer("a"));
        System.out.println(blockingQueue.offer("b"));
        System.out.println(blockingQueue.offer("c"));
        //設定阻塞時間,3L為時間長度,TimeUnit.SECONDS為時間單位。3S內未能進佇列,則自動結束
        System.out.println(blockingQueue.offer("d",3L, TimeUnit.SECONDS));
    }
}

總結

1.在多執行緒領域:所謂阻塞,在某些情況下會掛起執行緒(即阻塞),一旦條件滿足,被掛起的執行緒又會自動被喚起

2.為什麼需要 BlockingQueue? 在 concurrent 包釋出以前,在多執行緒環境下,我們每個程式設計師都必須去自己控制這些細節,尤其還要兼顧效率和執行緒安全,而這會給我們的程式帶來不小的複雜度。使用後我們不需要關心什麼時候需要阻塞執行緒,什麼時候需要喚醒執行緒,因為這一切 BlockingQueue 都給你一手包辦了