zoukankan      html  css  js  c++  java
  • Java 实践:生产者与消费者

    实践项目:生产者与消费者【经典多线程问题】

    问题引出:

      生产者和消费者指的是两个不同的线程类对象,操作同一个空间资源的情况。

    需求引出:

      —— 生产者负责生产数据,消费者负责取走数据

      —— 生产者生产完一组数据之后,消费者就要取走一组数据

    设置三个类:数据类、生产类、消费类;生产和消费类是线程类,同时操作同一个数据类;生产类负责每次向数据类中写入一组数据;消费类负责每次从数据类中取出一组数据。

     1 package hello;
     2 
     3 class Info {  // 数据类
     4     private String title ;
     5     private String content;
     6     public void setTitle(String title) {
     7         this.title = title ; 
     8     }
     9     public String getTitle() {
    10         return title ;
    11     }
    12     public String getContent() {
    13         return content ;
    14     }
    15     public void setContent(String content) {
    16         this.content = content ;
    17     }
    18     
    19 }
    20 
    21 class Producer implements Runnable { // 生成者类(线程)
    22     private Info info ;
    23     public Producer(Info info) {
    24         this.info = info ;
    25     }
    26     @Override
    27     public void run() {
    28         for (int x = 0 ; x < 100 ; x ++ ) {
    29             try {
    30                 Thread.sleep(200);
    31             } catch (InterruptedException e) {
    32                 e.printStackTrace();
    33             }
    34             if ( x % 2 == 0 ) {
    35                 this.info.setTitle("张三");
    36                 this.info.setContent("男");
    37             } else {
    38                 this.info.setTitle("王五");
    39                 this.info.setContent("男");
    40             }
    41         }
    42     }
    43 }
    44 
    45 class Consumer implements Runnable {
    46     private Info info ; 
    47     public Consumer(Info info) {
    48         this.info = info ;
    49     }
    50     @Override
    51     public void run() {
    52         for (int x = 0 ; x < 100 ; x ++) {
    53             try {
    54                 Thread.sleep(100);
    55             } catch (InterruptedException e) {
    56                 e.printStackTrace();
    57             }
    58             System.out.println(this.info.getTitle() + "——" + this.info.getContent());
    59         }
    60     }
    61 }
    62 
    63 public class TestDemo {
    64     public static void main(String[] args) throws Exception {
    65         Info info = new Info() ;
    66         new Thread(new Producer(info)).start();
    67         new Thread(new Consumer(info)).start();
    68     }
    69 }

     上例程序执行后,会发现“错位的现象”;出现类似数据为取走,就存入新的数据的错误。【不同步且异步现象导致】

     1 package hello;
     2 
     3 class Info {  // 数据类
     4     private String title ;
     5     private String content;
     6     public synchronized void set(String title , String content) {
     7         this.title = title ;
     8         try {
     9             Thread.sleep(100);
    10         } catch (InterruptedException e) {
    11             e.printStackTrace();
    12         }
    13  
    14         this.content = content ;
    15     }
    16     public synchronized void get() {
    17         try {
    18             Thread.sleep(100);
    19         } catch (InterruptedException e) {
    20             e.printStackTrace();
    21         }
    22         System.out.println(this.title + "——" + this.content);
    23     }
    24     
    25 }
    26 
    27 class Producer implements Runnable { // 生成者类(线程)
    28     private Info info ;
    29     public Producer(Info info) {
    30         this.info = info ;
    31     }
    32     @Override
    33     public void run() {
    34         for (int x = 0 ; x < 100 ; x ++ ) {                              
    35             if ( x % 2 == 0 ) {
    36                 this.info.set("张三", "男");
    37             } else {
    38                 this.info.set("李悦", "女");
    39             }
    40         }
    41     }
    42 }
    43 
    44 class Consumer implements Runnable {
    45     private Info info ; 
    46     public Consumer(Info info) {
    47         this.info = info ;
    48     }
    49     @Override
    50     public void run() {
    51         for (int x = 0 ; x < 100 ; x ++) {
    52             this.info.get();
    53         }
    54     }
    55 }
    56 
    57 public class TestDemo {
    58     public static void main(String[] args) throws Exception {
    59         Info info = new Info() ;
    60         new Thread(new Producer(info)).start();
    61         new Thread(new Consumer(info)).start();
    62     }
    63 }

    通过“同步方法”,解决了数据不同步的问题,但是于此而来的问题就是:数据的重复操作。

    针对上两例程序,我们通过Object类的支持,来解决数据重复操作的问题:
      如果像上例的设计,需要在程序中加入一个等待机制;当数据未取则等待数据取出后在存入,当数据未存等待数据存入后取出。而Object类中提供有专门的“等待”。

    等待:    public final void wait() throws InterruptedException
    唤醒第一个等待线程:    public final void notify() ;
    唤醒全部的等待进入:    public final void notifyAll();  //优先级高越有可能先唤醒

    通过Object的线程等待和唤醒功能完善程序:

     1 package hello;
     2 
     3 class Info {  // 数据类
     4     private String title ;
     5     private String content;
     6     private boolean flag = true ;
     7     // true:表示可以生产,不可以取走
     8     // false:表示不可以生产,可以取走
     9     public synchronized void set(String title , String content) {
    10         if (this.flag == false) { // 发现不可以生产,则等待线程
    11             try {
    12                 super.wait();  // 通过super调用自己的超类(父类)Object类中的wait()方法等待线程
    13             } catch (InterruptedException e) {
    14                 e.printStackTrace();
    15             }
    16         }
    17         this.title = title ;
    18         try {
    19             Thread.sleep(100);
    20         } catch (InterruptedException e) {
    21             e.printStackTrace();
    22         }
    23         this.content = content ;
    24         this.flag = false ;// 修改标记
    25         super.notify(); //唤醒其他等待线程
    26     }
    27     public synchronized void get() {
    28         if (this.flag == true) {
    29             try {
    30                 super.wait();
    31             } catch (InterruptedException e) {
    32                 e.printStackTrace();
    33             }
    34         }
    35         try {
    36             Thread.sleep(100);
    37         } catch (InterruptedException e) {
    38             e.printStackTrace();
    39         }
    40         System.out.println(this.title + "——" + this.content);
    41         this.flag = true ; //修改标记
    42         super.notify(); // 唤醒其他线程
    43     }
    44     
    45 }
    46 
    47 class Producer implements Runnable { // 生成者类(线程)
    48     private Info info ;
    49     public Producer(Info info) {
    50         this.info = info ;
    51     }
    52     @Override
    53     public void run() {
    54         for (int x = 0 ; x < 100 ; x ++ ) {                              
    55             if ( x % 2 == 0 ) {
    56                 this.info.set("张三", "男");
    57             } else {
    58                 this.info.set("李悦", "女");
    59             }
    60         }
    61     }
    62 }
    63 
    64 class Consumer implements Runnable {
    65     private Info info ; 
    66     public Consumer(Info info) {
    67         this.info = info ;
    68     }
    69     @Override
    70     public void run() {
    71         for (int x = 0 ; x < 100 ; x ++) {
    72             this.info.get();
    73         }
    74     }
    75 }
    76 
    77 public class TestDemo {
    78     public static void main(String[] args) throws Exception {
    79         Info info = new Info() ;
    80         new Thread(new Producer(info)).start();
    81         new Thread(new Consumer(info)).start();
    82     }
    83 }

     我们依靠Object类中的等待唤醒机制完成了代码的要求。

    ------------------------

  • 相关阅读:
    避免scrollview内部控件输入时被键盘遮挡,监听键盘弹起,配合做滚动
    红包功能的开发总结
    App启动时间分析
    第三方动画库 Lottie嵌入记录
    加入一个新的团队需要做什么
    OC 面向对象的特性
    为什么说OC是运行时语言?什么是动态类型、动态绑定、动态加载?
    adb pull 和 adb push
    《重构:改善既有代码的设计》重构的方法整理
    《重构:改善既有代码的设计》(二) 代码的坏味道
  • 原文地址:https://www.cnblogs.com/wangyuyang1016/p/10959373.html
Copyright © 2011-2022 走看看