Java多線程生產(chǎn)者消費(fèi)者模式實(shí)現(xiàn)過(guò)程解析
單生產(chǎn)者與單消費(fèi)者
示例:
public class ProduceConsume {
public static void main(String[] args) {
String lock = new String("");
Produce produce = new Produce(lock);
Consume consume = new Consume(lock);
new Thread(() -> {
while (true) {
produce.setValue();
}
}, "ProductThread").start();
new Thread(() -> {
while (true) {
consume.getValue();
}
}, "ConsumeThread").start();
}
/**
* 生產(chǎn)者
*/
static class Produce {
private String lock;
public Produce(String 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)者
*/
static class Consume {
private String lock;
public Consume(String 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();
}
}
}
static class ValueObject {
public static String value = "";
}
}
執(zhí)行結(jié)果如下:

多生產(chǎn)者與多消費(fèi)者
這種模式下,容易出現(xiàn)“假死”,也就是全部線程都進(jìn)入了 WAITNG 狀態(tài),程序不在執(zhí)行任何業(yè)務(wù)功能了,整個(gè)項(xiàng)目呈停止?fàn)顟B(tài)。
示例:
public class MultiProduceConsume {
public static void main(String[] args) throws InterruptedException {
String lock = new String("");
Produce produce = new Produce(lock);
Consume consume = new Consume(lock);
Thread[] pThread = new Thread[2];
Thread[] cThread = new Thread[2];
for (int i = 0; i < 2; i++) {
pThread[i] = new Thread(() -> {
while (true) {
produce.setValue();
}
}, "生產(chǎn)者" + (i + 1));
cThread[i] = new Thread(() -> {
while (true) {
consume.getValue();
}
}, "消費(fèi)者" + (i + 1));
pThread[i].start();
cThread[i].start();
}
Thread.sleep(5000);
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName() + " " + threadArray[i].getState());
}
}
static class Produce {
private String lock;
public Produce(String lock) {
this.lock = lock;
}
public void setValue() {
try {
synchronized (lock) {
while(!ValueObject.value.equals("")) {
System.out.println("生產(chǎn)者 " + Thread.currentThread().getName() + " WAITING了⭐");
lock.wait();
}
System.out.println("生產(chǎn)者 " + Thread.currentThread().getName() + " RUNNABLE了");
String value = System.currentTimeMillis() + "_" + System.nanoTime();
ValueObject.value = value;
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class Consume {
private String lock;
public Consume(String lock) {
this.lock = lock;
}
public void getValue() {
try {
synchronized (lock) {
while (ValueObject.value.equals("")) {
System.out.println("消費(fèi)者 " + Thread.currentThread().getName() + " WAITING了⭐");
lock.wait();
}
System.out.println("消費(fèi)者 " + Thread.currentThread().getName() + "RUNNABLE了");
ValueObject.value = "";
lock.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ValueObject {
public static String value = "";
}
}
運(yùn)行結(jié)果如圖:

分析:
雖然代碼中通過(guò) wait/notify 進(jìn)行通信了,但是不能保證 notify 喚醒的一定是異類,也可能是同類,比如“生產(chǎn)者”喚醒了“生產(chǎn)者”這樣的情況。
解決方案:
假死出現(xiàn)的主要原因是有可能連續(xù)喚醒了同類。所以解決方案很簡(jiǎn)單,就是把 notify() 改為 notifyAll() 即可。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
淺析springboot通過(guò)面向接口編程對(duì)控制反轉(zhuǎn)IOC的理解
這篇文章主要介紹了springboot通過(guò)面向接口編程對(duì)控制反轉(zhuǎn)IOC的理解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-08-08
C語(yǔ)言中下標(biāo)與指針的轉(zhuǎn)換以及指向指針的指針的例子
這篇文章主要介紹了C語(yǔ)言中下標(biāo)與指針的轉(zhuǎn)換以及指向指針的指針的示例,是C語(yǔ)言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11
java.Net.UnknownHostException異常處理問(wèn)題解決
這篇文章主要介紹了java.Net.UnknownHostException異常處理方法,問(wèn)題原因是在系統(tǒng)的?/etc/Hostname中配置了主機(jī)名,而在/etc/hosts文件中沒有相應(yīng)的配置,本文給大家詳細(xì)講解,需要的朋友可以參考下2023-03-03
基于SpringBoot實(shí)現(xiàn)文件秒傳功能
在開發(fā)Web應(yīng)用時(shí),文件上傳是一個(gè)常見需求,然而,當(dāng)用戶需要上傳大文件或相同文件多次時(shí),會(huì)造成帶寬浪費(fèi)和服務(wù)器存儲(chǔ)冗余,此時(shí)可以使用文件秒傳技術(shù)通過(guò)識(shí)別重復(fù)文件,本文就給大家介紹了如何基于SpringBoot實(shí)現(xiàn)文件秒傳功能,需要的朋友可以參考下2025-04-04
Java在web頁(yè)面上的編碼解碼處理及中文URL亂碼解決
這篇文章主要介紹了Java在web頁(yè)面上的編碼解碼處理及中文URL亂碼解決,文中所介紹的兩種使用過(guò)濾器解決中文鏈接亂碼的方法非常有效,需要的朋友可以參考下2016-02-02
Java中的ThreadLocal與ThreadLocalMap詳解
這篇文章主要介紹了Java中的ThreadLocal與ThreadLocalMap詳解,ThreadLocal 是一個(gè)線程局部變量,其實(shí)的功用非常簡(jiǎn)單,就是為每一個(gè)使用該變量的線程都提供一個(gè)變量值的副本,是Java中一種較為特殊的線程綁定機(jī)制,需要的朋友可以參考下2023-09-09
如何使用IDEA開發(fā)Spark SQL程序(一文搞懂)
Spark SQL 是一個(gè)用來(lái)處理結(jié)構(gòu)化數(shù)據(jù)的spark組件。它提供了一個(gè)叫做DataFrames的可編程抽象數(shù)據(jù)模型,并且可被視為一個(gè)分布式的SQL查詢引擎。這篇文章主要介紹了如何使用IDEA開發(fā)Spark SQL程序(一文搞懂),需要的朋友可以參考下2021-08-08
MyBatis 參數(shù)類型為String時(shí)常見問(wèn)題及解決方法
這篇文章主要介紹了MyBatis 參數(shù)類型為String時(shí)常見問(wèn)題及解決方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-03-03

