Java多線程:生產(chǎn)者與消費者案例
前言
想象一下生活中哪些是和線程沾邊的?飯店炒菜就是一個很好的例子
首先客人要吃菜,前提是廚師要炒好,也就是說,廚師不炒好的話客人是沒有飯菜的。這時候,廚師就是一個線程,客人拿菜就是另一個線程。
工具
jdk13,IDEA2019.1.4
知識點
Thread、Runnable、synchronized、面向?qū)ο笾R(繼承、封裝、接口、方法重寫)、條件判斷以及線程的一些其他知識點
設(shè)計思路
首先要有兩個線程,也就是說要兩個類,分別是Producer(生產(chǎn)者)和Consumer(消費者)。
由于我們是模擬廚師與客人之間的互動,也就是說還需要一個類來封裝信息:Message(類)。
然后,避免線程之間發(fā)生數(shù)據(jù)混亂的情況,肯定還需要使用synchronized來進行線程同步。
具體步驟
首先我們來一份沒有用synchronized的代碼,先看看效果:
public class Message {
private String title;
private String content;
Message(){
};
public Message(String title, String content) {
this.title = title;
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
/*
* 定義生產(chǎn)者類Producer
* */
class Producer implements Runnable{
private Message msg=null;
public Producer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i=0;i<=50;i++){
if (i%2==0){
this.msg.setTitle("喜歡夜雨嗎?");
try {
Thread.sleep(100);
}catch (InterruptedException e){
System.out.println(e);
}
this.msg.setContent("是的呢!");
}else {
this.msg.setTitle("還不關(guān)注我嗎?");
try {
Thread.sleep(100);
}catch (InterruptedException e){
System.out.println(e);
}
this.msg.setContent("好的呢!");
}
}
}
}
/*
* 定義消費者類Consumer
* */
class Consumer implements Runnable{
private Message msg=null;
public Consumer(Message msg) {
this.msg = msg;
}
@Override
public void run() {
for (int i=0;i<=50;i++){
try {
Thread.sleep(100);
}catch (InterruptedException e){
System.out.println(e);
}
System.out.println(this.msg.getTitle()+"--->"+this.msg.getContent());
}
}
}
class TestDemo{
public static void main(String[] args) {
Message msg=new Message();
new Thread(new Producer(msg)).start();
new Thread(new Consumer(msg)).start();
}
}
看看運行結(jié)果:

看仔細咯,發(fā)生了數(shù)據(jù)錯亂啊,Title與Content沒有一一對應(yīng)欸。咋辦?
能咋辦,改代碼唄。
發(fā)生數(shù)據(jù)混亂的原因完全是因為,生產(chǎn)者線程還沒生產(chǎn)的時候,消費者就已經(jīng)消費了(至于消費的啥我也不知道,我也不敢問啊)。所以造成了數(shù)據(jù)混亂,不過我們上面說了啊,要使用synchronized來讓線程同步一下。但是又會出問題,我們接著往下看
class TestDemo{
public static void main(String[] args) {
Message msg=new Message();
new Thread(new Producer(msg)).start();
new Thread(new Consumer(msg)).start();
}
}
class Message{
private String title,content;
public synchronized void set(String title,String content){
this.title=title;
this.content=content;
}
public synchronized void get(){
try {
Thread.sleep(1000);
}catch (InterruptedException e){
System.out.println(e);
}
System.out.println(this.title+"-->"+this.content);
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
class Producer implements Runnable{
private Message msg=null;
Producer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for (int i=0;i<=50;i++){
if (i%2==0){
this.msg.set("喜歡夜雨嗎?","是的呢!");
}else {
this.msg.set("還不關(guān)注嗎?","好的呢!");
}
}
}
}
class Consumer implements Runnable{
private Message msg=null;
Consumer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for (int i=0;i<=50;i++){
this.msg.get();
}
}
}
我又重新封裝了一些方法,然后運行的時候,wc,數(shù)據(jù)倒是不混亂了,但是呢,數(shù)據(jù)重復了一大堆。

為啥會出現(xiàn)這個問題呢?最后想了一下,會出現(xiàn)這種問題的,就是因為線程的執(zhí)行順序的問題。我們想要實現(xiàn)的效果是生產(chǎn)者線程執(zhí)行了之后,讓生產(chǎn)者線程等待,而后讓消費者線程執(zhí)行,等待消費者線程執(zhí)行完成之后就又讓生產(chǎn)者線程繼續(xù)執(zhí)行。后來我又查了查官方文檔,發(fā)現(xiàn)Object類中專門有三個方法是與線程相關(guān)的:
| 方法 | 描述 |
|---|---|
| public final void wait() throws InterruptedException | 線程的等待 |
| public final void notify() | 喚醒第一個等待線程 |
| public final void notifyAll() |
當我看到這些方法的時候,心里愣了一下,這不就是我們想要的方法嗎,用wait()方法來讓生產(chǎn)者線程等待,然后運行消費者線程,等消費者線程執(zhí)行完了之后又讓生產(chǎn)者線程去執(zhí)行。這其中我們用true和false來表示運行開始和運行暫停。
最后我們來看看完成品的代碼:
class TestDemo{
public static void main(String[] args) {
Message msg=new Message();
new Thread(new Producer(msg)).start();
new Thread(new Consumer(msg)).start();
}
}
class Message extends Exception{
private String title,content;
private boolean flag=true;
// true表示正在生產(chǎn),不要來取走,因為沒由讓消費者區(qū)走的
// false表示可以取走,但是不能生產(chǎn)
public synchronized void set(String title,String content){
if (this.flag==false){
try {
super.wait();
}catch (InterruptedException e){
System.out.println(e);
}
}
this.title=title;
try {
Thread.sleep(60);
}catch (InterruptedException e){
System.out.println(e);
}
this.content=content;
this.flag=true; // 生產(chǎn)完成,修改標志位
super.notify(); // 喚醒等待線程
}
public synchronized void get(){
if (flag==true) {// 已經(jīng)生產(chǎn)好了,等待取走
try {
super.wait();
}catch (InterruptedException e){
System.out.println(e);
}
}
try {
Thread.sleep(60);
}catch (InterruptedException e){
System.out.println(e);
}
System.out.println(this.title+"-->"+this.content);
this.flag=true;
super.notify();
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
class Producer implements Runnable{
private Message msg=null;
Producer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for (int i=0;i<=50;i++){
if (i%2==0){
this.msg.set("喜歡夜雨嗎?","是的呢!");
}else {
this.msg.set("還不關(guān)注嗎?","好的呢!");
}
}
}
}
class Consumer implements Runnable{
private Message msg=null;
Consumer(Message msg){
this.msg=msg;
}
@Override
public void run() {
for (int i=0;i<=50;i++){
this.msg.get();
}
}
}
運行結(jié)果我就不貼了,你們自己去測試一下吧…
總結(jié)
這個案例完美的呈現(xiàn)了線程以及面向?qū)ο笾R的綜合運用,具有很強的實際操作性
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和Sta
這篇文章主要介紹了Spring Boot啟動過程(六)之內(nèi)嵌Tomcat中StandardHost、StandardContext和StandardWrapper的啟動教程詳解,需要的朋友可以參考下2017-04-04
Java 使用JdbcTemplate 中的queryForList發(fā)生錯誤解決辦法
這篇文章主要介紹了Java 使用JdbcTemplate 中的queryForList發(fā)生錯誤解決辦法的相關(guān)資料,需要的朋友可以參考下2017-07-07
解決IDEA service層跳轉(zhuǎn)實現(xiàn)類的快捷圖標消失問題
這篇文章主要介紹了解決IDEA service層跳轉(zhuǎn)實現(xiàn)類的快捷圖標消失問題,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02
springboot+camunda實現(xiàn)工作流的流程分析
Camunda是基于Java語言,支持BPMN標準的工作流和流程自動化框架,并且還支持CMMN規(guī)范,DMN規(guī)范,本文給大家介紹springboot+camunda實現(xiàn)工作流的流程分析,感興趣的朋友一起看看吧2021-12-12

