阻塞隊列:原理、應用及實現

阻塞隊列:原理、應用及實現

    • 什么是阻塞隊列
    • 以生產消費者模型形象地理解阻塞隊列
    • 阻塞隊列實現生產消費者模型
    • 模擬實現阻塞隊列實現生產消費者模型

什么是阻塞隊列

阻塞隊列是一種特殊且實用的隊列數據結構,它同樣遵循 “先進先出” 的原則。與普通隊列不同的是,阻塞隊列是線程安全的,這使得它在多線程編程中扮演著重要角色。

阻塞隊列具有以下兩個關鍵特性:

  • 當隊列已滿時,如果有線程嘗試將元素入隊列,該線程將會被阻塞,直到有其他線程從隊列中取走元素,使得隊列騰出空間。
  • 當隊列為空時,若有線程試圖從隊列中出隊列,此線程也會被阻塞,直至有其他線程向隊列中插入新的元素。

阻塞隊列的一個經典應用場景便是 “生產者消費者模型”。這種模型在軟件開發中被廣泛使用,能夠有效地協調不同線程之間的工作,提高程序的性能和穩定性。

以生產消費者模型形象地理解阻塞隊列

為了更直觀地理解阻塞隊列的工作原理,我們以一個具體的生產消費者場景為例。假設有四個生產者線程和一個消費者線程:

  • 每個生產者線程每單位時間能夠生產 4 個面包。
  • 消費者線程每單位時間只能取走 1 個面包。
    在這里插入圖片描述

在這種情況下,由于生產者的生產速度遠快于消費者的消費速度,每單位時間會有 3 個面包進入阻塞隊列,等待消費者線程來取。并且在這個過程中,生產者線程不會停止生產。
在這里插入圖片描述

隨著時間的推移,當阻塞隊列被填滿,沒有空位時,生產者線程就會因為無法繼續入隊列而停止生產,直到消費者線程從隊列中取走一些面包,騰出空間。
在這里插入圖片描述

阻塞隊列實現生產消費者模型

在 Java 中,BlockingQueue接口及其實現類為我們提供了便捷的方式來實現生產消費者模型。以下是 BlockingQueue 的構造方法示例圖:

在這里插入圖片描述

下面通過一段 Java 代碼來具體實現生產消費者模型(為了便于觀察和理解,我們創建了一個固定容量的阻塞隊列):

import java.util.Random;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;public class Main {public static void main(String[] args) {Random random = new Random();// 首先創建一個阻塞隊列,容量為 10BlockingDeque<Integer> blockingDeque = new LinkedBlockingDeque<>(10);// 創建生產者線程Thread t1 = new Thread(() -> {while (true) {try {int value = random.nextInt(100);System.out.println("生產元素:" + value);blockingDeque.put(value);} catch (InterruptedException e) {throw new RuntimeException(e);}}});// 創建消費者線程Thread t2 = new Thread(() -> {while (true) {try {System.out.println("消費元素:" + blockingDeque.take());Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

通過上述代碼,生產者線程不斷生產隨機整數并放入阻塞隊列,而消費者線程則從隊列中取出元素并打印,同時每隔 1 秒消費一次,模擬實際的消費速度。
在這里插入圖片描述

模擬實現阻塞隊列實現生產消費者模型

除了使用 Java 提供的現成阻塞隊列實現類,我們還可以自己模擬實現一個固定容量的阻塞隊列。在這個實現過程中,需要重點關注以下三個方面:

  1. 確保線程安全,避免多個線程同時訪問隊列時出現數據不一致的問題。
  2. 當隊列為空時,消費者線程需要阻塞等待,直到隊列中有新的元素可供消費。
  3. 當隊列為滿時,生產者線程需要阻塞等待,直到隊列中有元素被消費,騰出空間。

以下是模擬實現的代碼:

public class MyBlockqueue {// 隊列的最大容量int max = 10;// 創建一個固定容量的數組來存儲隊列元素int[] arr;// 記錄隊列中已有元素的個數int num;// 隊列頭元素的下標,遵循先進先出原則int first;// 隊列尾元素的下標,遵循后進后出原則int end;public MyBlockqueue(int max) {this.max = max;arr = new int[max];}// 模擬實現入隊列操作public void put(int x) throws InterruptedException {// 檢查隊列是否已滿while (num >= max) {// 如果已滿,當前線程進入阻塞狀態synchronized (this) {wait();}}// 隊列未滿,添加元素synchronized (this) {arr[end] = x;end = (end + 1) % max;num++;// 喚醒其他可能在等待的線程notify();}}// 模擬實現出隊列操作public Integer take() throws InterruptedException {// 檢查隊列是否為空while (num == 0) {// 如果為空,當前線程進入阻塞狀態synchronized (this) {wait();}}// 隊列不為空,取出元素synchronized (this) {int tmp = arr[first];first = (first + 1) % max;num--;// 喚醒其他可能在等待的線程notify();return tmp;}}
}

接下來,我們使用這個自定義的阻塞隊列來實現生產消費者模型:

import java.util.Random;public class Main {public static void main(String[] args) {Random random = new Random();// 創建一個自定義的阻塞隊列,容量為 10MyBlockqueue blockingDeque = new MyBlockqueue(10);// 創建生產者線程Thread t1 = new Thread(() -> {while (true) {try {int value = random.nextInt(100);System.out.println("生產元素:" + value);blockingDeque.put(value);} catch (InterruptedException e) {throw new RuntimeException(e);}}});// 創建消費者線程Thread t2 = new Thread(() -> {while (true) {try {System.out.println("消費元素:" + blockingDeque.take());Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

在這里插入圖片描述

通過上述代碼,我們成功地模擬實現了一個阻塞隊列,并基于它構建了生產消費者模型,幫助我們更深入地理解阻塞隊列的工作原理和應用場景。

希望這篇博客能讓你對阻塞隊列有更全面、深入的認識!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/74262.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/74262.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/74262.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【開源寶藏】30天學會CSS - DAY5 第五課 脈沖動畫

以下是一個完整的漸進式教程&#xff0c;拆解如何用 HTML CSS 構建“Pulsar”水波脈沖動畫。通過閱讀&#xff0c;你將理解每個核心屬性與關鍵幀如何配合&#xff0c;讓一個小圓不斷散發動態波紋&#xff0c;并且文字始終停留在圓心。 第 0 步&#xff1a;項目概覽 文件結構示…

2060 裁紙刀

2060 裁紙刀 ??難度&#xff1a;簡單 &#x1f31f;考點&#xff1a;2022、規律、思維 &#x1f4d6; &#x1f4da; import java.util.Arrays; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner;public class Main {static int N 100010…

TextView、AppCompatTextView和MaterialTextView該用哪一個?Android UI 組件發展史與演進對照表

在 Android 開發中&#xff0c;UI 組件一直在不斷演進&#xff0c;從最初的原生組件&#xff0c;到 Support Library&#xff08;AppCompat 兼容庫&#xff09;&#xff0c;再到如今的 Material Design 組件。這篇文章將梳理 Android UI 組件的發展歷史&#xff0c;并提供詳細的…

python學習筆記--實現簡單的爬蟲(一)

任務&#xff1a;爬取豆瓣最受歡迎的250個電影的資料 鏈接&#xff1a;豆瓣電影 Top 250 用瀏覽器打開后&#xff0c;使用F12或鼠標右鍵--檢查&#xff0c;查看網頁的源代碼&#xff0c;分析網頁結構&#xff0c;如下圖所示&#xff1a; 分析后得知&#xff1a; 1.電影名位于…

Postgresql 刪除數據庫報錯

1、刪除數據庫時&#xff0c;報錯存在其他會話連接 ## 錯誤現象&#xff0c;存在其他的會話連接正在使用數據庫 ERROR: database "cs" is being accessed by other users DETAIL: There is 1 other session using the database.2、解決方法 ## 終止被刪除數據庫下…

self Attention為何除以根號dk?(全新角度)

全網最獨特解析&#xff1a;self Attention為何除根號dk&#xff1f; 一、假設條件&#xff1a;查詢向量和鍵向量服從正態分布 假設查詢向量 q i q_i qi?和鍵向量 k j k_j kj?的每個分量均為獨立同分布的隨機變量&#xff0c;且服從標準正態分布&#xff0c;即&#xff1a;…

numpy學習筆記10:arr *= 2向量化操作性能優化

numpy學習筆記10&#xff1a;arr * 2向量化操作性能優化 在 NumPy 中&#xff0c;直接對整個數組進行向量化操作&#xff08;如 arr * 2&#xff09;的效率遠高于顯式循環&#xff08;如 for i in range(len(arr)): arr[i] * 2&#xff09;。以下是詳細的解釋&#xff1a; 1. …

Cursor+Claude-3.5生成Android app

一、Android Studio下載 https://developer.android.com/studio?hlzh-tw#get-android-studio 等待安裝完成 二、新建工程 點擊new project 選擇Empty Activity 起一個工程名 當彈出這個框時 可以在settings里面選擇No proxy 新建好后如下 點擊右邊模擬器&#xff0c…

WPF Reactive 數據綁定

文章目錄 Combox 綁定List-通過枚舉綁定方法一:方法二:Button 綁定TextBlock綁定NumericUpDown綁定Expander綁定checkbox綁定NumericUpDownCombox 綁定List-通過枚舉綁定 方法一: ViewControl using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; usin…

算法及數據結構系列 - 滑動窗口

系列文章目錄 算法及數據結構系列 - 二分查找 算法及數據結構系列 - BFS算法 算法及數據結構系列 - 動態規劃 算法及數據結構系列 - 雙指針 算法及數據結構系列 - 回溯算法 算法及數據結構系列 - 樹 文章目錄 滑動窗口框架思路經典題型76. 最小覆蓋子串567. 字符串的排列438. …

Android adb調試應用程序

啟動app 有的時候app不是預先安裝的&#xff0c;也不能從界面start一個app&#xff0c;這時需要后臺拉起app。 $adb shell am start package.name/Activity.name 例如&#xff0c;android原生camera app&#xff0c; 包名為com.android.camera2&#xff0c; mainActivity名為…

Java EE(15)——網絡原理——TCP協議解析一

一.確認應答/(確認)序列號 接收方接收到數據后&#xff0c;向發送方返回一個確認信號(ack)&#xff0c;告訴發送方數據被成功接收。ACK報文段只是作為確認使用的&#xff0c;一般來說不攜帶應用層數據&#xff08;載荷&#xff09;&#xff0c;也就是說只有報頭部分。但有可能…

node-ddk,electron 組件, 打開新窗口

node-ddk 打開新窗口 https://blog.csdn.net/eli960/article/details/146207062 也可以下載demo直接演示 http://linuxmail.cn/go#node-ddk 本文講解如何在渲染進程發起創建新窗口, 包括 window.open 在主進程定義窗口類型 import main, { NODEDDK } from "node-ddk…

git管理時keil項目忽略文件列表

在使用 Git 管理 Keil MDK&#xff08;μVision 5&#xff09;工程時&#xff0c;需要忽略編譯生成的臨時文件、調試文件、用戶配置等非必要內容。以下是忽略文件的詳細列表及說明&#xff0c;可直接保存為 .gitignore 文件&#xff1a; Keil MDK 工程的 .gitignore 文件 giti…

C#單例模式

單例模式 (Singleton),保證一個類僅有一個實例&#xff0c;并提供一個訪問它的全局訪問點。通常我們可以讓一個全局變量使得一個對象被訪問&#xff0c;但它不能防止你實例化對個對象&#xff0c;一個最好的辦法就是&#xff0c;讓類自身負責保護它的唯一實例。這個類可以保證沒…

ZYNQ的cache原理與一致性操作

在Xilinx Zynq SoC中&#xff0c;Cache管理是確保處理器與外部設備&#xff08;如FPGA邏輯、DMA控制器&#xff09;之間數據一致性的關鍵。Zynq的ARM Cortex-A9處理器包含L1 Cache&#xff08;指令/數據&#xff09;和L2 Cache&#xff0c;其刷新&#xff08;Flush/Invalidate&…

Linux NFS、自動掛載與系統啟動管理指南

1. NFS客戶端掛載導出的目錄的方式 NFS&#xff08;網絡文件系統&#xff09; 允許將遠程服務器的目錄掛載到本地&#xff0c;像訪問本地文件一樣操作遠程文件。掛載方式主要有兩種&#xff1a; 手動掛載&#xff1a;使用 mount 命令&#xff08;臨時生效&#xff0c;重啟后丟…

NO.55十六屆藍橋杯備戰|排序|插入|選擇|冒泡|堆|快速|歸并(C++)

插?排序 插?排序(Insertion Sort)類似于玩撲克牌插牌過程&#xff0c;每次將?個待排序的元素按照其關鍵字??插?到前?已排好序的序列中&#xff0c;按照該種?式將所有元素全部插?完成即可 #include <iostream> using namespace std; const int N 1e5 10; …

【Oracle資源損壞類故障】:詳細了解壞塊

目錄 1、物理壞塊與邏輯壞塊 1.1、物理壞塊 1.2、邏輯壞塊 2、兩個壞塊相關的參數 2.1、db_block_checksum 2.2、db_block_checking 3、檢測壞塊 3.1、告警日志 3.2、RMAN 3.3、ANALYZE 3.4、數據字典 3.5、DBVERIFY 4、修復壞塊 4.1、RMAN修復 4.2、DBMS_REPA…

計算機網絡高頻(二)TCP/IP基礎

計算機網絡高頻(二)TCP/IP基礎 1.什么是TCP/IP?? TCP/IP是一種網絡通信協議,它是互聯網中最常用的協議之一。TCP/IP有兩個基本的協議:TCP(傳輸控制協議)和IP(互聯網協議)。 TCP(Transmission Control Protocol,傳輸控制協議)是一種可靠的、面向連接的協議。它負…