多線程案例-阻塞隊列

阻塞隊列是什么

阻塞隊列是一種特殊的隊列.也遵循"先進先出"的原則

阻塞隊列能是一種線程安全的數據結構,并且具有以下特性:

當隊列滿的時候,繼續入隊列就會阻塞,直到有其他線程從隊列中取走元素.

當隊列空的時候,繼續出隊列也會阻塞,直到有其他線程往隊列中插入元素.

阻塞隊列的一個典型應用場景就是"生產者消費者模型".這時一種非常典型的開發模型.

生產者消費者模型

實際開發中,經常會涉及到分布式系統.服務器整個功能不是由一個服務器全部完成的.而是每個服務器負責一部分功能.通過服務器間的網絡通信,最終完成整個功能.

生產者消費者模型就是通過一個容器來解決生產者和消費者的強耦合問題.(更好地做到解耦合的能力).

生產者和消費者彼此之間不再進行通訊,而是通過阻塞隊列來進行通訊,所以生產者生產完數據不用再等消費者處理,而是直接丟給阻塞隊列,消費者不找生產者要數據,而是直接從阻塞隊列中取.

示意圖如下:

1.阻塞隊列就相當于一個緩沖區,平衡了生產者和消費者的處理能力.(削峰填谷)

比如在"秒殺"的場景下,服務器同一時刻可能會受到大量的支付請求.如果直接處理這些支付要求,服務器可能扛不住(每個支付請求的處理都需要比較復雜的流程,即使一個請求消耗的資源少,但加到一起,總的消耗的資源就多了,任何一種硬件資源達到瓶頸,服務器都會掛).這個時候就可以把這些請求都放到一個阻塞隊列中,然后再由消費者線程慢慢來處理每個支付請求.?

這樣做可以有效做到"削峰",防止服務器被突然來到的一波請求直接沖垮(掛的直觀現象:給它發請求,無回應).

2.阻塞隊列也能使生產者和消費者之間"解耦"

比如過年一家人一起包餃子.一般都是有明確分工,比如一個人負責搟餃子皮,其他人負責包.搟餃子皮的人就是"生產者",包餃子的人就是"消費者".

搟餃子皮的人并不關心包餃子的人是誰(能包就行,無論是手工,借助工具還是機器),包餃子的人也不需要關心搟餃子皮的人是誰(有餃子皮就行,無論是用搟面杖搟的,還是用ipadAir5搟的)

補充說明:

(1)上述描述的阻塞隊列,并非是簡單的數據結構,而是基于這個這個數據結構實現的服務器程序,又被部署到單獨的主機上了(消息隊列)

(2)整個系統的結構更復雜了.你要維護的服務器更多了

(3)效率.引入中間商,還是有差價的.比如在上面的圖當中,請求從A出來到B收到.過程中的就經歷隊列的轉發,這個過程有一定開銷.

標準庫中的阻塞隊列

在Java標準庫中內置了阻塞隊列.如果我們需要在一些程序中使用阻塞隊列,直接使用標準庫中的即可.

譬如有:ArrayBlockingQueue, LinkedBlockingQueue,PriorityBlockingQueue.但最常用的是

LinkedBlockingQueue.

BlockingQueue是一個接口.真正實現的類是LinkedBlockingQueue.

put方法用于阻塞式的入隊列,take用于阻塞式的出隊列.

BlockingQueue也有offer,poll,peek等方法,但是這些方法不具有阻塞特性.

簡單的代碼示例:

public class BlockingQueueTest {public static void main(String[] args) throws InterruptedException {BlockingQueue<String> queue = new LinkedBlockingQueue<>();//入隊列queue.put("abc");//出隊列.如果沒有put直接take,會阻塞.String elem = queue.take();System.out.println(elem);}
}

生產者消費者模型

實際開發中,生產者消費者模型,往往是多個生產者多個消費者.

這里的生產者和消費者往往不僅是一個線程,也可能是獨立的服務器程序.甚至是一組服務器程序.?

代碼示例如下:

public class TestCustomerAndProducer {public static void main(String[] args) {BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>();Thread customer = new Thread(() -> {while(true) {try {int value = blockingQueue.take();System.out.println("消費元素: " + value);Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}}, "消費者");Thread producer = new Thread(() -> {Random r = new Random();while(true) {try {int num = r.nextInt(1000);System.out.println("生產元素: " + num);blockingQueue.put(num);Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}}}, "生產者");customer.start();producer.start();}
}

?阻塞隊列的實現

通過"循環隊列"的方式實現.

使用synchronized進行加鎖控制.

put插入元素的時候,判定如果隊列滿了,就進行wait.(注意,要在循環中進行wait.被喚醒時不一定隊列就不滿了,因為同時可能是喚醒了多個線程).

take取出元素的時候如果判定隊列為空,就進行wait(也是循環wait).

下面展示代碼(注意注釋中的重點):

public class MyBlockingQueue {//主題內容指定為一個含有1000個元素的數組public int[] elems = new int[1000];private volatile int size = 0;private volatile int head = 0;private volatile int tail = 0;//鎖對象private Object locker = new Object();public synchronized int getSize() {return size;}public void put(int value) throws InterruptedException {//鎖加到這里和加到方法上的本質是一樣的,加到方法上是給this加鎖,此處是給locker對象加鎖.synchronized (locker) {while(size >= elems.length) {//1//隊列滿了//后續需要讓這個代碼能夠阻塞locker.wait();}//新的元素要放到tail指向的位置上elems[tail] = value;tail = (tail + 1) % elems.length;size++;//入隊之后喚醒(可能有阻塞的take方法)locker.notify();}}public int take() throws InterruptedException {int ret = 0;synchronized (locker) {while(size <= 0) {//1//隊列空了//后續也需要讓這個代碼阻塞locker.wait();}//取出head位置的元素并返回ret = elems[head];head = (head + 1) % elems.length;size--;//元素出隊列成功后,加上喚醒locker.notify();}return ret;}
}

我相信大家應該能了解鎖是怎么加的,這里不過多贅述.

那可能就會有人問,1處的判斷處為什么用的是while,而不是if?

?這主要是因為put和take中使用的是同一把鎖.我們可能會想到,如put中元素滿了阻塞,然后take出元素了,這里就解鎖.但我要說的是,如果是put成功了(這里put之后隊列剛好滿了),又喚醒了另一個阻塞的put,又進行put,顯然會出錯,那么就可以加上循環條件,如果隊列一直是滿的,就不再被喚醒,所以用while.

?

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

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

相關文章

這七款網工在線畫拓撲工具,絕了!

你們好&#xff0c;我的網工朋友。 畫拓撲圖&#xff0c;絕對是網絡工程師的基操。 上次給你來了篇手把手教你繪制拓撲圖的好文&#xff0c;還沒看過的先去看啊&#xff1a;《網絡拓撲圖怎么畫最好&#xff1f;》。 關于畫拓撲的工具&#xff0c;那就多了&#xff0c;直接用…

數據結構與算法-D8D9隊列實現及應用

隊列&#xff1a;限制在兩端進行插入和刪除的線性表 允許進行存入操作的一端為“隊尾” 允許進行刪除操作的一端為“隊頭” 順序隊列 注意&#xff1a;front指向隊頭元素的位置 rear指向隊尾元素的下一個位置 實現循環隊列&#xff1a;(rear1)%N取余&#xff0c;為了區分空…

Connection refused: no further information

解決目錄 一、報錯信息二、解決方法 一、報錯信息 二、解決方法 1、報錯原因是開啟了代理&#xff0c;像AS是絕對不能開代理的。 2、設置為No proxy&#xff0c;然后Apply再選擇OK&#xff0c;重新同步。 要遠離消耗你的人和事&#xff0c;不要花費任何情緒或者精力在他們身…

unity Pc獲取本機Mac地址

1.此方法只能獲取眾多Mac中的一個 private static string GetMacAddress(){string physicalAddress "";NetworkInterface[] nice NetworkInterface.GetAllNetworkInterfaces();foreach (NetworkInterface adaper in nice){Debug.Log(adaper.Description);if (adape…

Linux網絡——高級IO

目錄 一.五種IO模型 1.阻塞式IO 2.非阻塞式IO 3.信號驅動IO 4.多路轉接IO&#xff1a; 5.異步IO 二.同步通信 vs 異步通信 三.設置非阻塞IO 1.阻塞 vs 非阻塞 2.非阻塞IO 3.實現函數SetNoBlock 四.I/O多路轉接之select 1.初識select 2.select函數原型 3.socket就緒…

UEFI下Windows10和Ubuntu22.04雙系統安裝圖解

目錄 簡介制作U盤啟動盤并從U盤啟動電腦安裝系統安裝Windows系統安裝Ubuntu 附錄雙系統時間不一致 簡介 傳統 Legacy BIOS主板下的操作系統安裝可參考本人博客 U盤系統盤制作與系統安裝&#xff08;詳細圖解&#xff09; &#xff0c;本文介紹UEFI主板下的雙系統安裝&#xff…

手把手教你在GPU T4卡上安裝硬解環境+編譯硬解的ffmpeg

系列文章目錄 文章目錄 系列文章目錄前言一、NVDIA環境軟件安裝二、FFMPEG編譯過程總結前言 通常開發流媒體服務,經常需要ffmpeg支持硬解解碼功能,即常見的GPU解碼,如cuda解碼等。下面主要講解在全新的環境中怎么安裝nvidia的環境與編譯ffmpeg的過程。 運行環境Centos7.5 G…

解決 Element-ui中 表格(Table)使用 v-if 條件切換后,表格的列的篩選不顯示了

解決方法 在每個需要使用 v-if 或 v-else 的 el-table-column 上增加 key 作為唯一標識&#xff0c;這樣渲染的時候就不會因為復用原則導致列數據混亂了。關于key值&#xff0c;一般習慣使用字段名&#xff0c;也可隨機生成一個值&#xff0c;只要具有唯一性就可以。

如何快速上手不熟悉的庫

首先需要一個編輯器vscode或者pycharm 然后&#xff0c;不要傻乎乎的自己急著去看代碼。 先看有沒有文檔和使用手冊&#xff0c;一般都有一個quick_start.md文件或者其他的.md文件。 然后&#xff0c;還是不急著看代碼&#xff0c;先看代碼的注釋。 比如我現在要從這里找到…

Java王者榮耀火柴人

主要功能 鍵盤W,A,S,D鍵&#xff1a;控制玩家上下左右移動。按鈕一&#xff1a;控制英雄發射一個矩形攻擊紅方小兵。按鈕控制英雄發射魅惑技能&#xff0c;傷害小兵并讓小兵停止移動。技能三&#xff1a;攻擊多個敵人并讓小兵停止移動。普攻&#xff1a;對小兵造成基礎傷害。小…

LVGL——按鈕部件

目錄 一、組成部分 二、按鈕部件操作 1、創建 2、設置樣式 3、添加事件 4、代碼例程 三、按鈕部件案例 一、組成部分 主體&#xff08;LV_PART_MAIN&#xff09; 二、按鈕部件操作 1、創建 lv_obj_t *btn lv_btn_create( parent );2、設置樣式 lv_obj_set_siz…

nginx配置自動壓縮-gzip壓縮

1.nginx配置文件 server里添加gzip配置信息。 重啟nginx服務 對比效果&#xff1a;上圖是沒有開啟gzip自動壓縮&#xff0c;總共資源是1.3M&#xff0c;傳輸1.3MB&#xff0c;下圖是開啟gzip壓縮&#xff0c;總共資源是1.3M&#xff0c;傳輸了973KB。

Axure簡單安裝與入門

目錄 一.Axure簡介 二.應用場景 三.安裝與漢化 3.1.安裝 3.2.漢化 四. 入門 4.1.復制、剪切及粘貼區域 4.2.選擇模式 4.3. 插入形狀 4.4.預覽、共享 感謝大家觀看&#xff01;希望能幫到你哦&#xff01;&#xff01;&#xff01; 一.Axure簡介 Axure RP是一款專業的原型…

四種數據庫執行腳本文件導入數據的方式

執行腳本文件的方式 Mysql mysql執行sql腳本文件的方法&#xff1a; 1、在命令行輸入mysql -uroot -h10.235.5.55 -p’123456’ -P3306 < F:\hello\niuzi.sql 2、在命令行輸入【source F:\hello\niuzi.sql】 mysql -uroot -h10.235.5.55 -p’123456’ -P3306 -e "sou…

HarmonyOS4.0從零開始的開發教程10管理組件狀態

HarmonyOS&#xff08;八&#xff09;管理組件狀態 概述 在應用中&#xff0c;界面通常都是動態的。如圖1所示&#xff0c;在子目標列表中&#xff0c;當用戶點擊目標一&#xff0c;目標一會呈現展開狀態&#xff0c;再次點擊目標一&#xff0c;目標一呈現收起狀態。界面會根…

ERROR: [BD 41-237] Bus Interface property FREQ_HZ does not match between

在自定義IP出現以上錯誤時可以通過雙擊模塊clk屬性 如果是灰色無法二次編輯時&#xff0c;在封裝IP時&#xff0c;選擇以下菜單

復雜sql分析 以及 索引合并

復雜sql分析 簡單的sql語句我們很簡單的就可以分析出來它的執行計劃&#xff0c;但是復雜的sql呢。例如 SELECT * FROM single_table WHERE(key1 > xyz AND key2 748 ) OR(key1 < abc AND key1 > lmn) OR(key1 LIKE %suf AND key1 > zzz AND (key2 < 8000 OR…

為什么同一張顯卡,深度學習的模型訓練的時候,有時候成功了,有時候失敗了

在同一張顯卡上進行深度學習模型訓練時&#xff0c;成功或失敗的結果可能受到以下因素的影響&#xff1a; 隨機性: 深度學習模型中的一些組件&#xff08;如權重初始化、數據的隨機排列等&#xff09;可能涉及到隨機性。這可能導致在每次訓練時得到不同的結果&#xff0c;有時成…

Mybatis進階知識

Mybatis的事務管理機制 在mybatis-config.xml文件中.可以進行mybatis的事務管理 <transactionManager type"JDBC"/> type的值有兩個 JDBCMANAGED JDBC事務管理器 mybatis框架自己管理事務&#xff0c;自己采用原生的JDBC代碼去管理事務 底層創建的事務管…