java併發包之阻塞隊列BlockingQueue

1. 原理概述

  • 阻塞隊列(BlockingQueue)是一個支持兩個附加操作的隊列。這兩個附加的操作是:在隊列爲空時,獲取元素的線程會等待隊列變爲非空。當隊列滿時,存儲元素的線程會等待隊列可用。
  • 阻塞隊列常用於生產者和消費者的場景,生產者是往隊列裏添加元素的線程,消費者是從隊列裏拿元素的線程。阻塞隊列就是生產者存放元素的容器,而消費者也只從容器裏拿元素。
    這裏寫圖片描述
  • 如圖,負責生產的線程不斷的製造新對象並插入到阻塞隊列中,直到達到這個隊列的上限值。隊列達到上限值之後生產線程將會被阻塞,直到消費的線程對這個隊列進行消費。同理,負責消費的線程不斷的從隊列中消費對象,直到這個隊列爲空,當隊列爲空時,消費線程將會被阻塞,除非隊列中有新的對象被插入。

2. 核心方法

(1) 插入數據

  1. offer(anObject)
    表示如果可能的話,將anObject加到BlockingQueue裏,即如果BlockingQueue可以容納,則返回true,否則返回false.(本方法不阻塞當前執行方法的線程)
  2. offer(E o, long timeout, TimeUnit unit)
    可以設定等待的時間,如果在指定的時間內,還不能往隊列中加入BlockingQueue,則返回失敗。
  3. put(anObject)
    把anObject加到BlockingQueue裏,如果BlockQueue沒有空間,則調用此方法的線程被阻塞,直到BlockingQueue裏面有空間再繼續。
    Offer和put的區別是,前者如果操作不能馬上進行,將會返回一個特殊的值,一般是true或者false,並不阻塞;後者如果操作不能馬上進行,操作會被阻塞。

(2) 獲取數據

  1. poll()
    取走BlockingQueue裏排在首位的對象,若能則立即取出, 取不到時返回null(不會被阻塞)。
  2. poll(long timeout, TimeUnit unit)
    從BlockingQueue取出一個隊首的對象,如果在指定時間內,隊列一旦有數據可取,則立即返回隊列中的數據。否則直到時間超時還沒有數據可取,返回失敗。
  3. take()
    取走BlockingQueue裏排在首位的對象,若BlockingQueue爲空,阻塞進入等待狀態直到 BlockingQueue有新的數據被加入。
  4. drainTo()
    一次性從BlockingQueue獲取所有可用的數據對象(還可以指定獲取數據的個數), 通過該方法,可以提升獲取數據效率;不需要多次分批加鎖或釋放鎖。

3. 阻塞隊列種類

這裏寫圖片描述

  1. ArrayBlockingQueue
    ArrayBlockingQueue是一個有邊界的阻塞隊列,它的內部實現是一個數組。有邊界的意思是它的容量是有限的,我們必須在其初始化的時候指定它的容量大小,容量大小一旦指定就不可改變。ArrayBlockingQueue是以先進先出的方式存儲數據(FIFO),最新插入的對象是尾部,最新移出的對象是頭部。

  2. LinkedBlockingQueue
    LinkedBlockingQueue阻塞隊列大小的配置是可選的,如果我們初始化時指定一個大小,它就是有邊界的,如果不指定,它就是無邊界的。說是無邊界,其實是採用了默認大小爲Integer.MAX_VALUE的容量 。它的內部實現是一個鏈表。
    ArrayBlockingQueue與ArrayList相比,LinkedBlockingQueue與LinkedList相比,都十分相似,但是前者比後者具有更好的性能。
    ArrayBlockingQueue和LinkedBlockingQueue是兩個最普通也是最常用的阻塞隊列,一般情況下,在處理多線程間的生產者消費者問題,使用這兩個類足以。

  3. DelayQueue
    DelayQueue中的元素只有當其指定的延遲時間到了,才能夠從隊列中獲取到該元素。DelayQueue是一個沒有大小限制的隊列,因此往隊列中插入數據的操作(生產者)永遠不會被阻塞,而只有獲取數據的操作(消費者)纔會被阻塞。
    使用場景:
    DelayQueue使用場景較少,但都相當巧妙,常見的例子比如使用一個DelayQueue來管理一個超時未響應的連接隊列。

  4. PriorityBlockingQueue
    PriorityBlockingQueue是一個沒有邊界的、基於優先級的阻塞隊列,它的排序規則和 java.util.PriorityQueue一樣。需要注意,PriorityBlockingQueue中允許插入null對象。
    所有插入PriorityBlockingQueue的對象必須實現 java.lang.Comparable接口,隊列優先級的排序規則就是按照我們對這個接口的實現來定義的。

  5. SynchronousQueue
    SynchronousQueue是無緩衝的隊列,當一個線程往隊列裏插入一個元素後會被阻塞,除非這個元素被另一個線程消費。


4. 實例

  • 生產者與消費者模型
// 生產者
import java.util.concurrent.BlockingQueue;

public class Producer implements Runnable {
    private BlockingQueue<String> queue;

    public Producer(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " is making product...");
        String product = "made by " + Thread.currentThread().getName();
        try {
            queue.put(product);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
// 消費者
import java.util.concurrent.BlockingQueue;

public class Consumer implements Runnable{
    private BlockingQueue<String> queue;

    public Consumer(BlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            String  product = queue.take();
            System.out.println(Thread.currentThread().getName() + " is consuming product " + product + "...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
// 客戶端
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Client {
    public static void main(String[] args) {
        BlockingQueue<String> queue = new LinkedBlockingQueue<>(5);
        for (int i = 0; i < 2; i++) {
            new Thread(new Consumer(queue), "Producer" + i).start();
        }
        for (int i = 0; i < 5; i++) {
            // 只有兩個 Product,因此只能消費兩個,其它三個消費者被阻塞
            new Thread(new Producer(queue), "Consumer" + i).start();
        }
        for (int i = 2; i < 5; i++) {
            new Thread(new Consumer(queue), "Producer" + i).start();
        }
    }
}
// 運行結果
Consumer0 is making product...
Producer0 is consuming product made by Consumer0...
Consumer1 is making product...
Producer1 is consuming product made by Consumer1...
Consumer2 is making product...
Consumer3 is making product...
Consumer4 is making product...
Producer2 is consuming product made by Consumer2...
Producer3 is consuming product made by Consumer3...
Producer4 is consuming product made by Consumer4...
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章