Java多線程 生產(chǎn)者消費者模型實例詳解
生產(chǎn)者消費者模型
生產(chǎn)者:生產(chǎn)任務(wù)的個體;
消費者:消費任務(wù)的個體;
緩沖區(qū):是生產(chǎn)者和消費者之間的媒介,對生產(chǎn)者和消費者解耦。
當(dāng)
緩沖區(qū)元素為滿,生產(chǎn)者無法生產(chǎn),消費者繼續(xù)消費;
緩沖區(qū)元素為空,消費者無法消費,生產(chǎn)者繼續(xù)生產(chǎn);
wait()/notify()生產(chǎn)者消費者模型
制作一個簡單的緩沖區(qū)ValueObject,value為空表示緩沖區(qū)為空,value不為空表示緩沖區(qū)滿
public class ValueObject { public static String value = ""; }
生產(chǎn)者,緩沖區(qū)滿則wait(),不再生產(chǎn),等待消費者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(); } } }
消費者,緩沖區(qū)為空則wait(),等待生產(chǎn)者notify(),緩沖區(qū)為滿,消費者開始消費
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方法,啟動一個生產(chǎn)者和一個消費者
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(); } }
運行結(jié)果如下
Set的值是:1564733938518_27520480474279 Get的值是:1564733938518_27520480474279 Set的值是:1564733938518_27520480498378 Get的值是:1564733938518_27520480498378 Set的值是:1564733938518_27520480540254 Get的值是:1564733938518_27520480540254 ······
生產(chǎn)者和消費者交替運行,生產(chǎn)者生產(chǎn)一個字符串,緩沖區(qū)為滿,消費者消費一個字符串,緩沖區(qū)為空,循環(huán)往復(fù),滿足生產(chǎn)者/消費者模型。
await()/signal()生產(chǎn)者/消費者模型
緩沖區(qū)
public class ValueObject { public static String value = ""; }
ThreadDomain48繼承ReentrantLock,set方法生產(chǎn),get方法消費
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() + "消費了value, value的當(dāng)前值是" + ValueObject.value); condition.signal(); } catch (InterruptedException e) { e.printStackTrace(); } finally { unlock(); } } }
MyThread41啟動兩個生產(chǎn)線程和一個消費線程
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消費了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123
為什么Producer2無法生產(chǎn),消費者無法消費呢?是因為此時緩沖區(qū)為滿,Producer1的notify()應(yīng)該喚醒Consumer卻喚醒了Producer2,導(dǎo)致Producer2因為緩沖區(qū)為滿和Consumer沒有被喚醒而處于waiting狀態(tài),此時三個線程均在等待,出現(xiàn)了假死。
解決方案有兩種:
1.讓生產(chǎn)者喚醒所有線程,在set方法中使用condition.signalAll();
2.使用兩個Condition,生產(chǎn)者Condition和消費者Condition,喚醒指定的線程;
正常輸入如下:
······ Producer2生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 Producer2生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 Producer2生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 Producer1生產(chǎn)了value, value的當(dāng)前值是123 Consumer消費了value, value的當(dāng)前值是 ······
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳解Mybatis 傳遞參數(shù)類型為List的取值問題
這篇文章主要介紹了詳解Mybatis 傳遞參數(shù)類型為List的取值問題,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10mybatis-plus查詢無數(shù)據(jù)問題及解決
這篇文章主要介紹了mybatis-plus查詢無數(shù)據(jù)問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-12-12Spring線程池ThreadPoolExecutor配置并且得到任務(wù)執(zhí)行的結(jié)果
今天小編就為大家分享一篇關(guān)于Spring線程池ThreadPoolExecutor配置并且得到任務(wù)執(zhí)行的結(jié)果,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03Spring boot 集成 Druid 數(shù)據(jù)源過程詳解
這篇文章主要介紹了Spring boot 集成 Druid 數(shù)據(jù)源過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2019-08-08