Java多线程:生产者与消费者案例
目录
- 前言
- 工具
- 知识点
- 设计思路
- 具体步骤
- 总结
前言 想象一下生活中哪些是和线程沾边的?饭店炒菜就是一个很好的例子
首先客人要吃菜,前提是厨师要炒好,也就是说,厨师不炒好的话客人是没有饭菜的。这时候,厨师就是一个线程,客人拿菜就是另一个线程。
工具
jdk13,IDEA2019.1.4
知识点
Thread、Runnable、synchronized、面向对象知识(继承、封装、接口、方法重写)、条件判断以及线程的一些其他知识点
设计思路
首先要有两个线程,也就是说要两个类,分别是Producer(生产者)和Consumer(消费者)。
由于我们是模拟厨师与客人之间的互动,也就是说还需要一个类来封装信息:Message(类)。
然后,避免线程之间发生数据混乱的情况,肯定还需要使用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; }}/** 定义生产者类Producer* */class Producer implements Runnable{private Message msg=null; public Producer(Message msg) {this.msg = msg; }@Overridepublic 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("还不关注我吗?"); 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; }@Overridepublic 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(); }}
看看运行结果:
文章图片
看仔细咯,发生了数据错乱啊,Title与Content没有一一对应欸。咋办?
能咋办,改代码呗。
发生数据混乱的原因完全是因为,生产者线程还没生产的时候,消费者就已经消费了(至于消费的啥我也不知道,我也不敢问啊)。所以造成了数据混乱,不过我们上面说了啊,要使用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; }@Overridepublic void run() {for (int i=0; i<=50; i++){if (i%2==0){this.msg.set("喜欢夜雨吗?","是的呢!"); }else {this.msg.set("还不关注吗?","好的呢!"); }}}}class Consumer implements Runnable{private Message msg=null; Consumer(Message msg){this.msg=msg; }@Overridepublic void run() {for (int i=0; i<=50; i++){this.msg.get(); }}}
我又重新封装了一些方法,然后运行的时候,wc,数据倒是不混乱了,但是呢,数据重复了一大堆。
文章图片
为啥会出现这个问题呢?最后想了一下,会出现这种问题的,就是因为线程的执行顺序的问题。我们想要实现的效果是生产者线程执行了之后,让生产者线程等待,而后让消费者线程执行,等待消费者线程执行完成之后就又让生产者线程继续执行。后来我又查了查官方文档,发现Object类中专门有三个方法是与线程相关的:
方法 | 描述 |
---|---|
public final void wait() throws InterruptedException | 线程的等待 |
public final void notify() | 唤醒第一个等待线程 |
public final void notifyAll() |
最后我们来看看完成品的代码:
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表示正在生产,不要来取走,因为没由让消费者区走的// false表示可以取走,但是不能生产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; // 生产完成,修改标志位super.notify(); // 唤醒等待线程}public synchronized void get(){if (flag==true) {// 已经生产好了,等待取走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; }@Overridepublic void run() {for (int i=0; i<=50; i++){if (i%2==0){this.msg.set("喜欢夜雨吗?","是的呢!"); }else {this.msg.set("还不关注吗?","好的呢!"); }}}}class Consumer implements Runnable{private Message msg=null; Consumer(Message msg){this.msg=msg; }@Overridepublic void run() {for (int i=0; i<=50; i++){this.msg.get(); }}}
运行结果我就不贴了,你们自己去测试一下吧…
总结 这个案例完美的呈现了线程以及面向对象知识的综合运用,具有很强的实际操作性
【Java多线程:生产者与消费者案例】本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注脚本之家的更多内容!
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 放屁有这三个特征的,请注意啦!这说明你的身体毒素太多
- 爱就是希望你好好活着
- 昨夜小楼听风
- 知识
- 死结。
- 我从来不做坏事
- 烦恼和幸福
- 关于QueryWrapper|关于QueryWrapper,实现MybatisPlus多表关联查询方式
- 事件代理