Java多线程:生产者与消费者案例

作者:鱼小洲 时间:2021-06-30 05:47:08 

前言

想象一下生活中哪些是和线程沾边的?饭店炒菜就是一个很好的例子

首先客人要吃菜,前提是厨师要炒好,也就是说,厨师不炒好的话客人是没有饭菜的。这时候,厨师就是一个线程,客人拿菜就是另一个线程。

工具

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;
   }
   @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("还不关注我吗?");
               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();
   }
}

看看运行结果:

Java多线程:生产者与消费者案例

看仔细咯,发生了数据错乱啊,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;
   }
   @Override
   public 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;
   }
   @Override
   public void run() {
       for (int i=0;i<=50;i++){
           this.msg.get();
       }
   }
}

我又重新封装了一些方法,然后运行的时候,wc,数据倒是不混乱了,但是呢,数据重复了一大堆。

Java多线程:生产者与消费者案例

为啥会出现这个问题呢?最后想了一下,会出现这种问题的,就是因为线程的执行顺序的问题。我们想要实现的效果是生产者线程执行了之后,让生产者线程等待,而后让消费者线程执行,等待消费者线程执行完成之后就又让生产者线程继续执行。后来我又查了查官方文档,发现Object类中专门有三个方法是与线程相关的:

方法描述
public final void wait() throws InterruptedException线程的等待
public final void notify()唤醒第一个等待线程
public final void notifyAll()

当我看到这些方法的时候,心里愣了一下,这不就是我们想要的方法吗,用wait()方法来让生产者线程等待,然后运行消费者线程,等消费者线程执行完了之后又让生产者线程去执行。这其中我们用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表示正在生产,不要来取走,因为没由让消费者区走的
   // 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;
   }
   @Override
   public 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;
   }
   @Override
   public void run() {
       for (int i=0;i<=50;i++){
           this.msg.get();
       }
   }
}

运行结果我就不贴了,你们自己去测试一下吧…

来源:https://blog.csdn.net/weixin_43581288/article/details/104784227

标签:Java,多线程,案例
0
投稿

猜你喜欢

  • Spring JPA 增加字段执行异常问题及解决

    2023-06-25 23:55:58
  • springboot读取配置文件中的参数具体步骤

    2023-11-29 05:46:14
  • C# 在PDF中添加墨迹注释Ink Annotation的步骤详解

    2022-01-31 14:23:29
  • 关于C#中yield return用法的思考

    2021-11-30 14:05:40
  • 浅谈Java编程之if-else的优化技巧总结

    2023-06-02 23:28:12
  • Java遍历Map对象的四种方式

    2022-05-07 14:30:04
  • Java经验点滴:类注释文档编写方法

    2023-11-06 03:50:11
  • Java中使用Lambda表达式和函数编程示例

    2022-05-06 03:30:40
  • springboot aop配合反射统一签名验证实践

    2023-08-04 08:14:07
  • 详解Java线程堆栈

    2021-06-29 03:37:38
  • 利用logback 设置不同包下的日志级别

    2022-08-11 20:05:43
  • 详解如何将Spring Boot应用跑在Docker容器中

    2023-04-25 08:08:58
  • SpringBoot MongoDB 索引冲突分析及解决方法

    2023-01-23 06:28:05
  • Android中使用Toast.cancel()方法优化toast内容显示的解决方法

    2021-12-14 05:17:03
  • kill命令在Java应用中使用的注意事项小结

    2023-11-11 13:01:55
  • Java编程实现获取当前代码行行号的方法示例

    2021-06-28 06:51:14
  • JavaWeb开发使用Cookie创建-获取-持久化、自动登录、购物记录、作用路径

    2022-01-11 10:37:28
  • Mybatis如何解决sql中like通配符模糊匹配问题

    2023-12-22 19:39:52
  • C#读取系统字体颜色与大小的方法

    2021-12-29 07:54:57
  • SpringBoot 文件上传和下载的实现源码

    2021-05-28 14:12:46
  • asp之家 软件编程 m.aspxhome.com