java中生產(chǎn)者消費者問題和代碼案例
應(yīng)用場景
假設(shè)倉庫中只能存放一件產(chǎn)品,生產(chǎn)者將生產(chǎn)出來的產(chǎn)品放入倉庫,消費者將倉庫中產(chǎn)品取走消費
如果倉庫中沒有產(chǎn)品,則生產(chǎn)者將產(chǎn)品放入倉庫,否則停止生產(chǎn)并等待,直到倉庫中的產(chǎn)品被消費者取走為止
如果倉庫中放有產(chǎn)品,則消費者可以將產(chǎn)品取走消費,否則停止消費并等待,直到倉庫中再次放入產(chǎn)品為止
分析
這是一個線程同步問題,生產(chǎn)者和消費者共享同一個資源,并且生產(chǎn)者和消費者之間相互依賴,互為條件。
對于生產(chǎn)者,沒有生產(chǎn)產(chǎn)品之前,要通知消費者等待,而生產(chǎn)了產(chǎn)品之后,又需要馬上通知消費者消費
對于消費者,在消費之后,要通知生產(chǎn)者已經(jīng)結(jié)束消費,需要生產(chǎn)新的產(chǎn)品以供消費
在生產(chǎn)者消費者問題中,僅有synchronized是不夠的
synchronized可阻止并發(fā)更新同一個共享資源,實現(xiàn)了同步
synchronized不能用來實現(xiàn)不同線程之間的消息傳遞(通信)
Java提供了幾個方法解決線程之間的通信問題
| 方法名 | 作用 |
|---|---|
| wait() | 表示線程一直等待,直到其他線程通知,與sleep不同,wait()會釋放鎖 |
| wait(long timeout) | 指定等待的毫秒數(shù) |
| notify() | 喚醒一個處于等待狀態(tài)的線程 |
| notifyAll() | 喚醒同一個對象上所有調(diào)用wait()方法的線程,優(yōu)先級別高的線程優(yōu)先調(diào)度 |
注意:均是Object類的方法,都只能在同步方法或者同步代碼塊中使用,否則會拋出異常
解決方法
管程法
生產(chǎn)者:負(fù)責(zé)生產(chǎn)數(shù)據(jù)的模塊(可能是方法、對象、線程、進程)
消費者:負(fù)責(zé)處理數(shù)據(jù)的模塊(可能是方法、對象、線程、進程)
緩沖區(qū):消費者不能直接使用生產(chǎn)者的數(shù)據(jù),他們之間有個“緩沖區(qū)”
生產(chǎn)者將生產(chǎn)好的數(shù)據(jù)放入緩沖區(qū),消費者從緩沖區(qū)拿出數(shù)據(jù)
代碼案例
//使用緩沖區(qū)解決生產(chǎn)者消費者模型
public class TestPC {
public static void main(String[] args) {
SynContainer container = new SynContainer();
new Producer(container).start();
new Consumer(container).start();
}
}
?
class Producer extends Thread{
SynContainer synContainer;
public Producer(SynContainer synContainer){
this.synContainer = synContainer;
}
// 生產(chǎn)
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synContainer.push(new Chicken(i));
System.out.println("生產(chǎn)了"+i+"只雞");
}
}
}
?
class Consumer extends Thread{
SynContainer synContainer;
public Consumer(SynContainer synContainer){
this.synContainer = synContainer;
}
// 消費
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消費了-->"+synContainer.pop().id+"只雞");
}
}
}
?
//產(chǎn)品
class Chicken{
int id;
public Chicken(int id) {
this.id = id;
}
}
?
//緩沖區(qū)
class SynContainer{
Chicken[] chickens = new Chicken[10];
int count = 0;
//生產(chǎn)者放入產(chǎn)品
public synchronized void push(Chicken chicken){
//如果容器滿了就要等到消費者消費
if (count==chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
//通知消費者消費,自身進入等待
}
//沒有滿的話則生產(chǎn)
chickens[count] = chicken;
count++;
//通知消費者消費
this.notifyAll();
}
?
//生產(chǎn)者消費產(chǎn)品
public synchronized Chicken pop(){
if (count==0){
//等待生產(chǎn)者生產(chǎn)
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
count--;
Chicken chicken = chickens[count];
//消費了,可以通知生產(chǎn)者繼續(xù)生產(chǎn)
this.notifyAll();
return chicken;
}
}信號燈法
通過標(biāo)志位 true 或者 false 來進行判斷
代碼案例
//信號燈法測試生產(chǎn)者消費者模型 即標(biāo)志位
public class TestPC2 {
public static void main(String[] args) {
TV tv = new TV();
new Player(tv).start();
new Audiance(tv).start();
}
?
}
?
//生產(chǎn)者-->演員
class Player extends Thread {
TV tv;
public Player(TV tv){
this.tv = tv;
}
?
@Override
public void run() {
for (int i = 0; i < 20; i++) {
if (i%2==0){
this.tv.play("天天向上");
}else {
this.tv.play("抖音廣告");
}
}
}
}
?
//消費者-->觀眾
class Audiance extends Thread{
TV tv;
public Audiance(TV tv){
this.tv = tv;
}
?
@Override
public void run() {
for (int i = 0; i < 20; i++) {
tv.watch();
}
}
}
?
//產(chǎn)品-->節(jié)目
class TV{
//演員表演 觀眾等待 T
//觀眾觀看 演員等待 F
String show;//表演的節(jié)目
boolean flag = true;
?
//表演
public synchronized void play(String show){
if (!flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("演員表演了:"+show);
//通知觀眾
this.notifyAll();
this.show = show;
this.flag = !this.flag;
}
?
//觀看
public synchronized void watch(){
if (flag){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("觀看了"+show);
//通知演員表演
this.notifyAll();
this.flag = !this.flag;
}
}總結(jié)
到此這篇關(guān)于java中生產(chǎn)者消費者問題和代碼案例的文章就介紹到這了,更多相關(guān)java生產(chǎn)者消費者問題內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用?mybatis?自定義日期類型轉(zhuǎn)換器的示例代碼
這篇文章主要介紹了使用?mybatis?自定義日期類型轉(zhuǎn)換器的示例代碼,這里使用mybatis中的typeHandlers?實現(xiàn)的,本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-03-03
MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶崿F(xiàn)
這篇文章主要介紹了MyBatis的mapper.xml文件中入?yún)⒑头祷刂档膶崿F(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-01-01
SpringBoot如何配置Controller實現(xiàn)Web請求處理
這篇文章主要介紹了SpringBoot如何配置Controller實現(xiàn)Web請求處理,文中通過圖解示例介紹的很詳細(xì),具有有一定的參考價值,需要的小伙伴可以參考一下2023-05-05
如何將二進制文件流轉(zhuǎn)化為MockMultipartFile文件
文章主要介紹了如何使用Spring框架中的MockMultipartFile類來模擬文件上傳,并處理上傳邏輯,包括獲取二進制文件流、創(chuàng)建MockMultipartFile對象、處理上傳邏輯和返回響應(yīng),還解釋了MockMultipartFile的功能和二進制文件流的定義2025-02-02

