亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

阻塞隊列 BlockingQueue

1. 前言

本節帶領大家認識第三個常用的 Java 并發容器類之 BlockingQueue。

本節先介紹 BlockingQueue 工具類表達的概念和最基本用法,接著通過一個例子為大家解釋 BlockingQueue 工具類的使用場合,然后通過簡單的編碼實現此場景。

下面我們正式開始介紹吧。

2. 概念解釋

BlockingQueue 顧名思義,就是阻塞隊列。隊列的概念同我們日常生活中的隊列一樣。在計算機中,隊列具有先入先出的特征,不允許插隊的情況出現。在 Java 中,BlockingQueue 是一個 interface 而非 class,它有很多實現類,如 ArrayBlockingQueue、LinkedBlockingQueue 等,這些實現類之間主要區別體現在存儲結構或元素操作上,但入隊和出隊操作卻是類似的。

概念已經了解了,BlockingQueue 工具類最基本的用法是怎樣的呢,我們以 LinkedBlockingQueue 實現類為例說明?看下面。

3. 基本用法

// 創建一個 LinkedBlockingQueue 對象
LinkedBlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue();
// 在不違反容量限制的情況下,可立即將指定元素插入此隊列,成功返回 true,當無可用空間時候,返回 IllegalStateException 異常。
linkedBlockingQueue.add("car");
// 在不違反容量限制的情況下,可立即將指定元素插入此隊列,成功返回 true,當無可用空間時候,返回 false。
linkedBlockingQueue.offer("car");
// 直接在隊列中插入元素,當無可用空間時候,阻塞等待。
linkedBlockingQueue.put("car");
// 將給定元素在給定的時間內設置到隊列中,如果設置成功返回 true,否則返回 false,超時后返回 fase。
linkedBlockingQueue.offer("car", 10, Timeunit.SECONDS);
// 獲取并移除隊列頭部的元素,無元素時候阻塞等待。
linkedBlockingQueue.take();
// 獲取并移除隊列頭部的元素,無元素時候阻塞等待指定時間。超時后返回 null。
linkedBlockingQueue.poll(10, Timeunit.SECONDS);

是不是很簡單,那 BlockingQueue 應用在哪些場合比較合適呢?下面我們給出最常用的場景說明。

4. 常用場景

BlockingQueue 首先作為一個隊列,可以適用于任何需要隊列數據結構的場合,其次其具有阻塞操作的特征,可用于線程間協同操作的場合。日常研發中,生產者消費者模型常常使用 BlockingQueue 實現。我們通過一張圖了解其基本邏輯。
圖片描述
我們舉一個生活中汽車排隊加油的例子說明:每一個加油站臺就是一個阻塞隊列,汽車依次排隊進入,先進入的先出站。當站臺滿了后繼車輛就需要排隊等待,當前面的汽車加好油離開(出隊)后,后面的汽車進入(入隊)開始加油。我們用 LinkedBlockingQueue 工具類實現,請看下面代碼。

5. 場景案例

import lombok.SneakyThrows;

import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;

public class BlockingQueueTest {

    // 創建一個 LinkedBlockingQueue 對象,用于汽車排隊
    private static LinkedBlockingQueue<String> linkedBlockingQueue = new LinkedBlockingQueue<>();

    // 主線程
    public static void main(String[] args) throws InterruptedException {
		// 汽車
        int carNumber = 1;
        while(carNumber < 5) {
            new Thread(new Runnable() {
                @SneakyThrows
                public void run() {
                    // 模擬用時
                    Thread.sleep(new Random().nextInt(1000));
                    // 汽車進站排隊等待
                    linkedBlockingQueue.offer(Thread.currentThread().getName());
                    System.out.println(Thread.currentThread().getName() + ":已經排隊進入收費站臺,等候收費...");
                }
            }, "汽車" + carNumber++).start();
        }

        // 收費員
        new Thread(new Runnable() {
            @SneakyThrows
            public void run() {
                while(true) {
                    // 模擬用時
                    Thread.sleep(new Random().nextInt(1000));
                    // 汽車過收費后出站
                    String car = linkedBlockingQueue.take();
                    System.out.println(Thread.currentThread().getName() + ":汽車" + car + "已經收費完畢離開收費站臺");
                }
            }
        }, "收費員").start();

        // 信息展示
        new Thread(new Runnable() {
            @SneakyThrows
            public void run() {
                while(true) {
                    Thread.sleep(1000);
                    // 實時公示當前車流排隊情況
                    int howMany = linkedBlockingQueue.size();
                    System.out.println(Thread.currentThread().getName() + ":當前還" + howMany + "輛在等候過站");
                }
            }
        }, "大屏").start();

        Thread.sleep(1000000);
    }
}

運行上面代碼,我們觀察一下運行結果。

汽車1:已經排隊進入收費站臺,等候收費...
汽車2:已經排隊進入收費站臺,等候收費...
汽車4:已經排隊進入收費站臺,等候收費...
收費員:汽車汽車1已經收費完畢離開收費站臺
汽車3:已經排隊進入收費站臺,等候收費...
大屏:當前還3輛在等候過站
收費員:汽車汽車2已經收費完畢離開收費站臺
大屏:當前還2輛在等候過站
收費員:汽車汽車4已經收費完畢離開收費站臺
收費員:汽車汽車3已經收費完畢離開收費站臺
大屏:當前還0輛在等候過站

觀察結果,和我們的預期一致。

6. 小結

本節通過一個簡單的例子,介紹了 BlockingQueue 接口以及一個具體的實現類 LinkedBlockingQueue 的使用場景和基本用法,其他實現了 BlockingQueue 接口的實現類都類似。希望大家在學習過程中,多思考勤練習,早日掌握之。