Java_多線程_生產者消費者模型_互斥鎖,阻塞隊列

生產者消費者模型(Producer-Consumer Model)是計算機科學中一個經典的并發編程模型,用于解決多線程/多進程環境下的協作問題。

基本概念
生產者:負責生成數據或任務的實體
消費者:負責處理數據或執行任務的實體
緩沖區:生產者與消費者之間共享的數據存儲區域
模型特點
生產者與消費者以不同的速度運行
兩者通過共享的緩沖區進行通信
緩沖區有大小限制,可能滿或空
需要解決的問題
同步問題:
當緩沖區滿時,生產者需要等待
當緩沖區空時,消費者需要等待
互斥問題:
對緩沖區的訪問必須是互斥的,防止數據競爭
常見實現方式
使用信號量(Semaphore):空緩沖區信號量滿緩沖區信號量互斥信號量
使用條件變量(Condition Variable)和互斥鎖(Mutex)
使用阻塞隊列(高級語言中常用)

本篇我們使用互斥鎖和阻塞隊列來解決這個問題
在多線程的鎖中我們首先要避免的就是死鎖這個問題,在 Java 中,Lock 接口及其實現類(如 ReentrantLock)是在 JDK 5(Java 5) 引入的,屬于java.util.concurrent.locks 包的一部分。我們這里使用Java的synchronized實現
首先來分析問題,我們可以抽象的將生產者消費者問題想象為,廚師和顧客的問題:
顧客:

  1. 判斷桌子上是否有食物
  2. 如果沒有就等待
  3. 如果有就直接吃掉
  4. 吃完食物之后,通知廚師繼續做食物

廚師:

  1. 判斷桌子上是否有食物
  2. 如果桌子上有食物的話就等待
  3. 如果桌子上沒有食物的話就制作食物
  4. 將食物放置在桌子上
  5. 喚醒等待的顧客開始吃

分析完畢,首先我們應該先新建三個類分別為:Cook(廚師類),Customer(顧客類),Desk(桌子類)
初始化桌子:

  1. 初始化桌子上食物的標志,0為無食物,1為有食物
  2. 初始化顧客的上限,例如顧客最多吃10份食物
package Thread.Producer_Consumer;public class Desk {public static int FoodFlag=0;//食物當前的狀態表示當前桌子上是否有食物public static int count=10;//消費者最多可以吃10個食物public static Object lock=new Object();////創建一個鎖對象,用于生產者和消費者線程間的同步
}//由于是所有線程共同的變量所以我們使用static關鍵字修飾

接下來開始完成顧客線程

package Thread.Producer_Consumer;public class Customer extends  Thread{//消費者線程@Overridepublic void run() {while( true){synchronized(Desk.lock){// 檢查是否達到食物上限(count=0表示不能再吃)if(Desk.count==0){break;}else {// 檢查桌子上是否有食物(FoodFlag=1表示有食物)if (Desk.FoodFlag == 1) {System.out.println("顧客吃掉食物");Desk.FoodFlag = 0;//表示沒有食物Desk.count--;//剩余可吃食物數量減1System.out.println("顧客還可以吃"+Desk.count);Desk.lock.notifyAll();//喚醒lock鎖中所有等待的線程} else {//桌上沒有食物,則等待try {Desk.lock.wait();//釋放鎖,并進入等待狀態} catch (InterruptedException e) {throw new RuntimeException(e);}}}}}System.out.println("顧客吃飽了,結束消費!");}
}

廚師線程

package Thread.Producer_Consumer;public class Cook extends Thread{@Overridepublic void run() {while (true) {synchronized (Desk.lock){//如果消費者可以吃的食物的數量已經達到最大,那么則直接退出if (Desk.count==0) {break;}else{//如果桌子有食物,等待消費者進程if(Desk.FoodFlag==1){try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else{//如果桌子沒有食物System.out.println("生產者正在生產食物...");//設置桌子有食物Desk.FoodFlag=1;//喚醒消費者線程Desk.lock.notifyAll();}}}}}
}

最后創建一個測試類

package Thread.Producer_Consumer;public class Test {public static void main(String[] args) {Desk desk=new Desk();Customer f1=new Customer();Cook c1=new Cook();f1.setName("消費者1");c1.setName("生產者1");f1.start();c1.start();}
}

在這里插入圖片描述
下來我們使用阻塞隊列來實現一下:
桌子類:

package Thread.Producer_Consumer_2;public class Desk {public static int count=10;//消費者最多可以吃10個食物public static int Food_max=10;
}

廚師類:

package Thread.Producer_Consumer_2;import java.util.concurrent.ArrayBlockingQueue;public class Cook extends  Thread{ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue){this.queue=queue;}//構造方法,創建一個阻塞隊列@Overridepublic void run() {//廚師不斷將食物放進隊列中while(true){if(Desk.Food_max<=0){break;}else{try {queue.put("食物");System.out.println("廚師放了一個食物");Desk.Food_max--;} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
}

顧客類:

package Thread.Producer_Consumer_2;import Thread.Producer_Consumer.Desk;import java.util.concurrent.ArrayBlockingQueue;public class Customer extends  Thread{//消費者線程ArrayBlockingQueue<String> queue;public Customer(ArrayBlockingQueue<String> queue){this.queue=queue;}@Overridepublic void run() {while( true){if(Desk.count==0){System.out.println("顧客吃到上限了");break;}else{try {queue.take();System.out.println("消費者吃掉了一個食物");Desk.count--;} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
}

測試類:

package Thread.Producer_Consumer_2;import Thread.Producer_Consumer.Desk;import java.util.concurrent.ArrayBlockingQueue;public class Test {public static void main(String[] args) {ArrayBlockingQueue<String> list=new ArrayBlockingQueue<>(5);//創建一個阻塞隊列Customer f1=new Customer(list);Cook c1=new Cook(list);f1.setName("消費者1");c1.setName("生產者1");f1.start();c1.start();}
}

阻塞隊列總結:
在創建阻塞隊列的時候,需要創建實現類的對象,我們可以通過查看源碼的形式來看一下
在這里插入圖片描述
ArrayBlockingQueue實現了BlockingQueue這個接口
在這里插入圖片描述
BlockingQueue又繼承于Queue
在這里插入圖片描述
Queue又繼承于Collection
在這里插入圖片描述
Collection又繼承于Iterable,由此我們也就可以得出,阻塞隊列是可以通過for_each循環遍歷的
在這里插入圖片描述
回歸主題,在ArrayBlockingQueue中,有一個帶參的構造方法,由此來創建阻塞隊列,capacity代表阻塞隊列的容量(不要忘記了泛型代表了阻塞隊列的參數為哪種類型)
在這里插入圖片描述
同時,LinkBlockingQueue也實現了BlockingQueue這個接口
在這里插入圖片描述
由此可以得出一個圖:
在這里插入圖片描述

在這里插入圖片描述
在這里插入圖片描述
由于put()和take()方法都是自帶鎖的,所以我們并不用手動設置鎖,同時,由于我們代碼中的輸出語句在鎖的外面在這里插入圖片描述
這導致了輸出時偶爾并不能按照我們的想法進行輸出,但是執行時一定是正確的

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

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

相關文章

Vue3實現視頻播放彈窗組件,支持全屏播放,音量控制,進度條自定義樣式,適配瀏覽器小窗播放,視頻大小自適配,緩沖loading,代碼復制即用

效果圖組件所需VUE3代碼<template><div class"video-dialog" :class"fullScreen && video-dialog-full-screen"><el-dialogv-model"props.visible"draggable:show-close"false"title""centeralign-c…

LLM層歸一化:γβ與均值方差的協同奧秘

LLM層歸一化參數均值和方差;縮放和平移參數是什么 層歸一化(Layer Normalization,LN)是深度學習中用于穩定神經網絡訓練的一種歸一化技術 均值和方差參數用于對輸入數據進行標準化處理,即將輸入數據轉換為均值為0、方差為1的標準正態分布 縮放因子γ\gammaγ:標準化后…

智慧場景:定制開發開源AI智能名片S2B2C商城小程序賦能零售新體驗

摘要&#xff1a;智慧場景作為零售行業創新發展的關鍵載體&#xff0c;正深刻改變著消費者的生活方式。本文聚焦智慧零售模式下智慧場景的構建&#xff0c;以定制開發開源AI智能名片S2B2C商城小程序為切入點&#xff0c;深入探討其在零售企業選址布局、商業模式創新、經營理念轉…

QML WorkerScript

WorkerScript是QML中實現多線程編程的關鍵組件&#xff0c;它允許開發者將耗時操作移至后臺線程執行&#xff0c;避免阻塞主UI線程&#xff0c;從而提升應用響應速度和用戶體驗。本文將全面介紹WorkerScript的核心機制、使用方法和最佳實踐。WorkerScript核心機制WorkerScript通…

銳浪報表 Grid++Report 表頭表尾的隱藏

設計銳浪表格的模板時&#xff0c;可以通過設計多個表頭、表尾&#xff0c;表頭、表尾中放入打印控件&#xff0c;可以打印相關的數據。在真實打印時&#xff0c;可以通過打印時讓表頭、表尾隱藏或顯示&#xff0c;實現用戶的表格樣式。一、表頭的指定1、 表頭可以多個&#xf…

低速信號設計之 QSPI 篇

一、引言? 在服務器技術不斷演進的當下,對高效、穩定的數據存儲和傳輸需求日益增長。QSPI(Quad Serial Peripheral Interface)總線作為一種高速、串行的外圍設備接口,在服務器領域中發揮著關鍵作用。它為服務器中的各類存儲設備及部分外圍芯片與主處理器之間提供了快速可…

別只知道暴力循環!我從用戶名校驗功能中領悟到的高效字符集判斷法(1684. 統計一致字符串的數目)

別只知道暴力循環&#xff01;我從用戶名校驗功能中領悟到的高效字符集判斷法 &#x1f60e; 大家好&#xff0c;日常開發中&#xff0c;我們經常會遇到一些看似不起眼&#xff0c;卻能成為性能瓶頸的小模塊。今天&#xff0c;我想和大家分享一個我親身經歷的故事&#xff0c;…

力扣面試150題--在排序數組中查找元素的第一個和最后一個位置

Day 85 題目描述思路 當 nums[mid] < target 時&#xff0c;說明目標值在右側&#xff0c;移動左指針 left mid 1 當 nums[mid] > target 時&#xff0c;說明目標值可能在當前位置或左側&#xff0c;移動右指針 right mid - 1 循環結束后&#xff0c;left 指針會指向第…

C++實戰:人臉識別7大核心實例

計算機視覺實例應用 基于C++的人臉識別實例 以下是一些基于C++的人臉識別實例的示例和實現方法,涵蓋了多種技術和庫的應用。這些例子可以幫助開發者快速上手并實現人臉識別功能。 OpenCV 基礎人臉檢測 使用OpenCV的預訓練模型進行人臉檢測是入門級示例。OpenCV自帶Haar級聯…

Uniapp中使用vue3語法

在setup語法糖中調用uniapp的頁面生命周期 <script setup>import { onShow } from "dcloudio/uni-app"onShow(() > {//hanlder...}) </script>vue2混入在vue3中建議使用組合式API 新建baseHook.js import { ref } from "vue"; export fu…

C++vector(2)

2.vector深度剖析及模擬實現 2.1std::vector的核心框架接口的模擬實現bit::vector vector的模擬實現 2.2 使用memcpy拷貝問題 假設模擬實現的vector中的reserve接口中&#xff0c;使用memcpy進行的拷貝&#xff0c;以下代碼會發生什么問題&#xff1f; int main() {gxl::ve…

IPSec VPN -- 野蠻模式

一、野蠻模式簡介野蠻模式VPN是指IPsec VPN中IKE協商采用野蠻模式&#xff08;Aggressive Mode&#xff09;的虛擬專用網絡。它是IKE第一階段協商的一種方式&#xff0c;與主模式相對&#xff0c;具有協商速度快但安全性稍低的特點。以下是具體介紹&#xff1a;1、工作原理&…

rk3588開發板使用硬件編碼處理視頻

開發板默認下載的ffmpeg是通用版&#xff0c;無法調用rk3588的硬件編碼器&#xff0c;視頻編碼效率低。 nyanmisaka開發了用于jellyfin的ffmpeg&#xff0c;支持rk3588硬件編碼器&#xff0c;編譯方法&#xff1a; https://github.com/nyanmisaka/ffmpeg-rockchip/wiki/Compil…

`neutron router-gateway-set` 操作失敗的可能原因及解決方案

根據提供的錯誤信息和搜索結果&#xff0c;neutron router-gateway-set 操作失敗的可能原因及解決方案如下&#xff1a;一、常見錯誤原因數據庫字符集配置問題&#xff08;中文名支持&#xff09; 表現&#xff1a;若路由器名稱包含中文字符&#xff0c;可能因數據庫字符集非UT…

(一)ZooKeeper 發展歷史

?博客主頁&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客內容》&#xff1a;.NET、Java.測試開發、Python、Android、Go、Node、Android前端小程序等相關領域知識 &#x1f4e2;博客專欄&#xff1a; https://blog.csdn.net/m0_63815035/cat…

OpenCV快速入門之CV寶典

文章目錄OpenCV的基礎應用一、OpenCV簡介&#xff1a;1.1 OpenCV 優勢1.2 OpenCV-Python二、環境安裝2.1 環境導入三、圖像表示3.1 顏色空間&#xff08;Color Space&#xff09;3.2 具體說明3.3 圖像在計算機中的表示四、基本圖像操作4.1 創建窗口**1. 核心窗口行為控制**cv.W…

LangChain4j 兩種類型API

LangChain4j operates on two levels of abstraction: &#xfeff;LangChain4j 提供了兩種類型API抽象Low level. At this level, you have the most freedom and access to all the low-level components such as ChatModel, UserMessage, AiMessage, EmbeddingStore, Embedd…

CLI 與 IDE 編碼代理比較:提升開發效率的兩種路徑

引言 在當今快速發展的軟件開發領域&#xff0c;人工智能編碼助手已成為開發者工具箱中不可或缺的一部分。根據行業報告&#xff0c;使用AI編碼助手可以將開發速度提高55%以上&#xff0c;同時顯著提升代碼質量。目前市場上主要有兩種類型的編碼代理&#xff1a;集成在IDE中的代…

【STM32】FreeRTOS 任務的創建(二)

這篇文章在于 詳細解釋 FreeRTOS 中任務的創建過程&#xff0c;包括任務創建的本質過程、API 詳解、兩種創建方式&#xff08;動態/靜態&#xff09;、任務函數規范、常見錯誤及實踐建議。 這里參照&#xff1a;RTOS官方文檔&#xff1a;https://www.freertos.org/zh-cn-cmn-s…

軟考 系統架構設計師系列知識點之面向服務架構設計理論與實踐(9)

接前一篇文章:軟考 系統架構設計師系列知識點之面向服務架構設計理論與實踐(8) 所屬章節: 第15章. 面向服務架構設計理論與實踐 第3節 SOA的參考架構 15.3 SOA的參考架構 IBM的Websphere業務集成參考架構(如圖15-2所示,以下簡稱參考架構)是典型的以服務為中心的企業集…