Java 阻塞隊列中的常用方法及區別

Java 阻塞隊列中的常用方法及區別

前言

在阻塞隊列中有很多方法,它們的功能都非常相似,所以非常有必要對這些類似的方法進行辨析,本章採用分類的方式對阻塞隊列中常見的方法進行討論。

BlockingQueue 中最常用的和添加、刪除相關的 8 個方法,把它們分爲三組。這三組方法由於功能很類似,所以比較容易混淆。它們的區別僅在於特殊情況:當隊列滿了無法添加元素,或者是隊列空了無法移除元素時,不同組的方法對於這種特殊情況會有不同的處理方式:

  • 拋出異常:add、remove、element
  • 返回結果但不拋出異常:offer、poll、peek
  • 阻塞:put、take

項目環境

1.第一組方法

1.1 add 方法

add 方法是往隊列裏添加一個元素,如果隊列滿了,就會拋出異常來提示隊列已滿。示例代碼如下:

public class BlockingQueueMethodsDemo {

    public static void main(String[] args) {
        BlockingQueue<String> bQueue = new ArrayBlockingQueue(3);
        // 添加方法
        addMethod(bQueue);
    }

    private static void addMethod(BlockingQueue<String> bQueue) {
        bQueue.add("1");
        bQueue.add("2");
        bQueue.add("3");
        bQueue.add("4");
    }
}

執行結果:

Exception in thread "main" java.lang.IllegalStateException: Queue full
	at java.util.AbstractQueue.add(AbstractQueue.java:98)
	at java.util.concurrent.ArrayBlockingQueue.add(ArrayBlockingQueue.java:312)
	at com.csdn.blockingqueue.BlockingQueueMethodsDemo.addMethod(BlockingQueueMethodsDemo.java:22)
	at com.csdn.blockingqueue.BlockingQueueMethodsDemo.main(BlockingQueueMethodsDemo.java:15)

顯然在添加第 4 個元素的時候,超過了容量的限制,拋出異常。

1.2 remove 方法

remove 方法的作用是刪除元素,如果我們刪除的隊列是空的,由於裏面什麼都沒有,所以也無法刪除任何元素,那麼 remove 方法就會拋出異常。示例代碼如下:

    private static void removeMethod(BlockingQueue<String> bQueue) {
        bQueue.add("1");
        bQueue.add("2");
        bQueue.remove();
        bQueue.remove();
        bQueue.remove();
    }

執行結果:

Exception in thread "main" java.util.NoSuchElementException
	at java.util.AbstractQueue.remove(AbstractQueue.java:117)
	at com.csdn.blockingqueue.BlockingQueueMethodsDemo.removeMethod(BlockingQueueMethodsDemo.java:34)
	at com.csdn.blockingqueue.BlockingQueueMethodsDemo.main(BlockingQueueMethodsDemo.java:19)

當調用第三個 remove 方法是,隊列中的元素爲空,拋出異常。

1.3 element 方法

element 方法是返回隊列的頭部節點,但是並不刪除。和 remove 方法一樣,如果我們用這個方法去操作一個空隊列,想獲取隊列的頭結點,可是由於隊列是空的,我們什麼都獲取不到,會拋出和前面 remove 方法一樣的異常:NoSuchElementException。示例代碼如下:

    private static void elementMethod(BlockingQueue<String> bQueue) {
        bQueue.add("1");
        bQueue.add("2");
        System.out.println("元素:"+bQueue.element());
        bQueue.remove();
        System.out.println("元素:"+bQueue.element());
        bQueue.remove();
        System.out.println("元素:"+bQueue.element());
    }

執行結果:

元素:1
元素:2
Exception in thread "main" java.util.NoSuchElementException
	at java.util.AbstractQueue.element(AbstractQueue.java:136)
	at com.csdn.blockingqueue.BlockingQueueMethodsDemo.elementMethod(BlockingQueueMethodsDemo.java:45)
	at com.csdn.blockingqueue.BlockingQueueMethodsDemo.main(BlockingQueueMethodsDemo.java:20)

2.第二組方法

實際上我們通常並不想看到第一組方法拋出的異常,這時我們可以優先採用第二組方法。第二組方法相比於第一組而言要友好一些,當發現隊列滿了無法添加,或者隊列爲空無法刪除的時候,第二組方法會給一個提示,而不是拋出一個異常。

2.1 offer 方法

offer 方法用來插入一個元素,並用返回值來提示插入是否成功。如果添加成功會返回 true,而如果隊列已經滿了,此時繼續調用 offer 方法的話,它不會拋出異常,只會返回一個錯誤提示:false。示例代碼如下:

    private static void offerMethod(BlockingQueue<String> bQueue) {
        System.out.println(bQueue.offer("1"));
        System.out.println(bQueue.offer("2"));
        System.out.println(bQueue.offer("3"));
        System.out.println(bQueue.offer("4"));
        System.out.println("隊列長度:" + bQueue.size());
    }

執行結果:

true
true
true
false
隊列長度:3

2.2 poll 方法

poll 方法和第一組的 remove 方法是對應的,作用也是移除並返回隊列的頭節點。但是如果當隊列裏面是空的,沒有任何東西可以移除的時候,便會返回 null 作爲提示。正因如此,我們是不允許往隊列中插入 null 的,否則我們沒有辦法區分返回的 null 是一個提示還是一個真正的元素。示例代碼如下:

    private static void pollMethod(BlockingQueue<String> bQueue) {
        bQueue.offer("1");
        bQueue.offer("2");
        System.out.println("元素:" + bQueue.poll());
        System.out.println("元素:" + bQueue.poll());
        System.out.println("元素:" + bQueue.poll());
    }

執行結果:

元素:1
元素:2
元素:null

2.3 peek 方法

peek 方法和第一組的 element 方法是對應的,意思是返回隊列的頭元素但並不刪除。如果隊列裏面是空的,它便會返回 null 作爲提示。示例代碼如下:

    private static void peekMethod(BlockingQueue<String> bQueue) {
        bQueue.offer("1");
        bQueue.offer("2");
        System.out.println("元素:" + bQueue.peek());
        bQueue.poll();
        System.out.println("元素:" + bQueue.peek());
        bQueue.poll();
        System.out.println("元素:" + bQueue.peek());
    }

執行結果:

元素:1
元素:2
元素:null

3.第三組方法

第三組是阻塞隊列最大特色的 put 和 take 方法,這兩個方法在 《什麼是阻塞隊列(BlockingQueue)?》 中討論過,這裏就直接將內容複製過來,加上簡單的示例代碼。

3.1 put 方法

put 方法插入元素時,如果隊列沒有滿,那就和普通的插入一樣是正常的插入,但是如果隊列已滿,那麼就無法繼續插入,則阻塞,直到隊列裏有了空閒空間。如果後續隊列有了空閒空間,比如消費者消費了一個元素,那麼此時隊列就會解除阻塞狀態,並把需要添加的數據添加到隊列中。過程如圖所示:

put

示例代碼:

    private static void putMethod(BlockingQueue<String> bQueue) {
        try {
            bQueue.put("1");
            bQueue.put("2");
            bQueue.put("3");
            bQueue.put("4");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

bQueue.put("4"); 執行的時候,由於隊列容量爲 3,主線程會一直阻塞。

3.2 take 方法

take 方法的功能是獲取並移除隊列的頭結點,通常在隊列裏有數據的時候是可以正常移除的。可是一旦執行 take 方法的時候,隊列裏無數據,則阻塞,直到隊列裏有數據。一旦隊列裏有數據了,就會立刻解除阻塞狀態,並且取到數據。過程如圖所示:

take

示例代碼:

    private static void takeMethod(BlockingQueue<String> bQueue) {
        try {
            System.out.println("元素:" +bQueue.take());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

由於隊列爲空,執行之後,主線程會一直阻塞。

4.總結

組別 方法 含義 特點
第一組 add 添加一個元素 如果隊列滿,拋出異常 IllegalStateException
第一組 remove 返回並刪除隊列的頭節點 如果隊列空,拋出異常 NoSuchElementException
第一組 element 返回隊列頭節點 如果隊列空,拋出異常 NoSuchElementException
第二組 offer 添加一個元素 添加成功,返回 true,添加失敗,返回 false
第二組 poll 返回並刪除隊列的頭節點 如果隊列空,返回 null
第二組 peek 返回隊列頭節點 如果隊列空,返回 null
第三組 put 添加一個元素 如果隊列滿,阻塞
第三組 take 返回並刪除隊列的頭節點 如果隊列空,阻塞

本文討論了阻塞隊列中常見的 8 個方法,按照各自的特點分爲以下三組

  • 第一組的特點是在無法正常執行的情況下拋出異常
  • 第二組的特點是在無法正常執行的情況下不拋出異常,但會用返回值提示運行失敗
  • 第三組的特點是在遇到特殊情況時讓線程陷入阻塞狀態,等到可以運行再繼續執行

5.參考

  • 《Java 併發編程 78 講》- 徐隆曦
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章