Java多線程 生產(chǎn)者消費(fèi)者模型實(shí)例詳解
生產(chǎn)者消費(fèi)者模型
生產(chǎn)者:生產(chǎn)任務(wù)的個(gè)體;
消費(fèi)者:消費(fèi)任務(wù)的個(gè)體;
緩沖區(qū):是生產(chǎn)者和消費(fèi)者之間的媒介,對(duì)生產(chǎn)者和消費(fèi)者解耦。
當(dāng)
緩沖區(qū)元素為滿,生產(chǎn)者無(wú)法生產(chǎn),消費(fèi)者繼續(xù)消費(fèi);
緩沖區(qū)元素為空,消費(fèi)者無(wú)法消費(fèi),生產(chǎn)者繼續(xù)生產(chǎn);
wait()/notify()生產(chǎn)者消費(fèi)者模型
制作一個(gè)簡(jiǎn)單的緩沖區(qū)ValueObject,value為空表示緩沖區(qū)為空,value不為空表示緩沖區(qū)滿
public class ValueObject {
public static String value = "";
}
生產(chǎn)者,緩沖區(qū)滿則wait(),不再生產(chǎn),等待消費(fèi)者notify(),緩沖區(qū)為空則開始生產(chǎn)
public class Producer {
private Object lock;
public Producer(Object lock)
{
this.lock = lock;
}
public void setValue()
{
try
{
synchronized (lock)
{
if (!ValueObject.value.equals(""))
lock.wait();
String value = System.currentTimeMillis() + "_" + System.nanoTime();
System.out.println("Set的值是:" + value);
ValueObject.value = value;
lock.notify();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
消費(fèi)者,緩沖區(qū)為空則wait(),等待生產(chǎn)者notify(),緩沖區(qū)為滿,消費(fèi)者開始消費(fèi)
public class Customer {
private Object lock;
public Customer(Object lock)
{
this.lock = lock;
}
public void getValue()
{
try
{
synchronized (lock)
{
if (ValueObject.value.equals(""))
lock.wait();
System.out.println("Get的值是:" + ValueObject.value);
ValueObject.value = "";
lock.notify();
}
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
main方法,啟動(dòng)一個(gè)生產(chǎn)者和一個(gè)消費(fèi)者
public class Main {
public static void main(String[] args)
{
Object lock = new Object();
final Producer producer = new Producer(lock);
final Customer customer = new Customer(lock);
Runnable producerRunnable = new Runnable()
{
public void run()
{
while (true)
{
producer.setValue();
}
}
};
Runnable customerRunnable = new Runnable()
{
public void run()
{
while (true)
{
customer.getValue();
}
}
};
Thread producerThread = new Thread(producerRunnable);
Thread CustomerThread = new Thread(customerRunnable);
producerThread.start();
CustomerThread.start();
}
}
運(yùn)行結(jié)果如下
Set的值是:1564733938518_27520480474279 Get的值是:1564733938518_27520480474279 Set的值是:1564733938518_27520480498378 Get的值是:1564733938518_27520480498378 Set的值是:1564733938518_27520480540254 Get的值是:1564733938518_27520480540254 ······
生產(chǎn)者和消費(fèi)者交替運(yùn)行,生產(chǎn)者生產(chǎn)一個(gè)字符串,緩沖區(qū)為滿,消費(fèi)者消費(fèi)一個(gè)字符串,緩沖區(qū)為空,循環(huán)往復(fù),滿足生產(chǎn)者/消費(fèi)者模型。
await()/signal()生產(chǎn)者/消費(fèi)者模型
緩沖區(qū)
public class ValueObject {
public static String value = "";
}
ThreadDomain48繼承ReentrantLock,set方法生產(chǎn),get方法消費(fèi)
public class ThreadDomain48 extends ReentrantLock
{
private Condition condition = newCondition();
public void set()
{
try
{
lock();
while (!"".equals(ValueObject.value))
condition.await();
ValueObject.value = "123";
System.out.println(Thread.currentThread().getName() + "生產(chǎn)了value, value的當(dāng)前值是" + ValueObject.value);
condition.signal();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
unlock();
}
}
public void get()
{
try
{
lock();
while ("".equals(ValueObject.value))
condition.await();
ValueObject.value = "";
System.out.println(Thread.currentThread().getName() + "消費(fèi)了value, value的當(dāng)前值是" + ValueObject.value);
condition.signal();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
unlock();
}
}
}
MyThread41啟動(dòng)兩個(gè)生產(chǎn)線程和一個(gè)消費(fèi)線程
public class MyThread41 {
public static void main(String[] args)
{
final ThreadDomain48 td = new ThreadDomain48();
Runnable producerRunnable = new Runnable()
{
public void run()
{
for (int i = 0; i < Integer.MAX_VALUE; i++)
td.set();
}
};
Runnable customerRunnable = new Runnable()
{
public void run()
{
for (int i = 0; i < Integer.MAX_VALUE; i++)
td.get();
}
};
Thread ProducerThread1 = new Thread(producerRunnable);
ProducerThread1.setName("Producer1");
Thread ProducerThread2 = new Thread(producerRunnable);
ProducerThread2.setName("Producer2");
Thread ConsumerThread = new Thread(customerRunnable);
ConsumerThread.setName("Consumer");
ProducerThread1.start();
ProducerThread2.start();
ConsumerThread.start();
}
}
輸出結(jié)果如下
Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123
為什么Producer2無(wú)法生產(chǎn),消費(fèi)者無(wú)法消費(fèi)呢?是因?yàn)榇藭r(shí)緩沖區(qū)為滿,Producer1的notify()應(yīng)該喚醒Consumer卻喚醒了Producer2,導(dǎo)致Producer2因?yàn)榫彌_區(qū)為滿和Consumer沒有被喚醒而處于waiting狀態(tài),此時(shí)三個(gè)線程均在等待,出現(xiàn)了假死。
解決方案有兩種:
1.讓生產(chǎn)者喚醒所有線程,在set方法中使用condition.signalAll();
2.使用兩個(gè)Condition,生產(chǎn)者Condition和消費(fèi)者Condition,喚醒指定的線程;
正常輸入如下:
······ Producer2生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer2生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer2生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費(fèi)了value, value的當(dāng)前值是 ······
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Java?RabbitMQ消息隊(duì)列詳解常見問(wèn)題
- java中RabbitMQ高級(jí)應(yīng)用
- Java實(shí)現(xiàn)Kafka生產(chǎn)者消費(fèi)者代碼實(shí)例
- Java多線程并發(fā)生產(chǎn)者消費(fèi)者設(shè)計(jì)模式實(shí)例解析
- Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過(guò)程解析
- Java多線程 BlockingQueue實(shí)現(xiàn)生產(chǎn)者消費(fèi)者模型詳解
- Java編寫簡(jiǎn)易rabbitmq生產(chǎn)者與消費(fèi)者的代碼
相關(guān)文章
java數(shù)據(jù)結(jié)構(gòu)與算法數(shù)組模擬隊(duì)列示例詳解
這篇文章主要為大家介紹了java數(shù)據(jù)結(jié)構(gòu)與算法數(shù)組模擬隊(duì)列示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06
SpringBoot?整合?ElasticSearch操作各種高級(jí)查詢搜索
這篇文章主要介紹了SpringBoot?整合?ES?進(jìn)行各種高級(jí)查詢搜索的實(shí)踐記錄,本文主要圍繞?SpringBoot?整合?ElasticSearch?進(jìn)行各種高級(jí)查詢的介紹,需要的朋友可以參考下2022-06-06
Spring的同一個(gè)服務(wù)會(huì)加載多次的問(wèn)題分析及解決方法
這篇文章主要介紹了Spring的同一個(gè)服務(wù)為什么會(huì)加載多次,我們先來(lái)梳理一下?Web?容器中如何加載?Bean,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-10-10
Spring boot通過(guò)AOP防止API重復(fù)請(qǐng)求代碼實(shí)例
這篇文章主要介紹了Spring boot通過(guò)AOP防止API重復(fù)請(qǐng)求代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
application.yml和bootstrap.yml不生效的3種解決方案
SpringBoot默認(rèn)支持?properties(.properties) 和 YAML(.yml .yaml ) 配置文件,本文主要介紹了application.yml和bootstrap.yml不生效的3種解決方案,具有一定的參考價(jià)值,感興趣的可以了解一下2024-03-03
Java經(jīng)理與員工的差異實(shí)現(xiàn)方法
這篇文章主要介紹了Java經(jīng)理與員工的差異實(shí)現(xiàn)方法,需要的朋友可以參考下2014-03-03
Java 中Json中既有對(duì)象又有數(shù)組的參數(shù)如何轉(zhuǎn)化成對(duì)象(推薦)
Gson庫(kù)是一個(gè)功能強(qiáng)大、易于使用的Java序列化/反序列化庫(kù),它提供了豐富的API來(lái)支持Java對(duì)象和JSON之間的轉(zhuǎn)換,這篇文章主要介紹了Java 中Json中既有對(duì)象又有數(shù)組的參數(shù)如何轉(zhuǎn)化成對(duì)象,需要的朋友可以參考下2024-07-07
一文詳解SpringBoot中CommandLineRunner接口
Spring Boot的CommandLineRunner接口是一個(gè)函數(shù)式接口,用于在Spring Boot應(yīng)用程序啟動(dòng)后執(zhí)行一些初始化操作,它提供了一個(gè)run方法,該方法在應(yīng)用程序啟動(dòng)后被調(diào)用,本文給大家詳細(xì)介紹了SpringBoot中CommandLineRunner接口,需要的朋友可以參考下2023-10-10

