• 【Java并发编程】:生产者—消费者模型


    生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据。

        这里实现如下情况的生产--消费模型:

        生产者不断交替地生产两组数据“姓名--1 --> 内容--1”,“姓名--2--> 内容--2”,消费者不断交替地取得这两组数据,这里的姓名--1”和“姓名--2”模拟为数据的名称,“内容--1 ”和“内容--2 ”模拟为数据的内容。

         由于本程序中牵扯到线程运行的不确定性,因此可能会出现以下问题:

         1、假设生产者线程刚向数据存储空间添加了数据的名称,还没有加入该信息的内容,程序就切换到了消费者线程,消费者线程将把信息的名称和上一个信息的内容联系在一起;

         2、生产者生产了若干次数据,消费者才开始取数据,或者是,消费者取完一次数据后,还没等生产者放入新的数据,又重复取出了已取过的数据。

         问题1很明显要靠同步来解决,问题2则需要线程间通信,生产者线程放入数据后,通知消费者线程取出数据,消费者线程取出数据后,通知生产者线程生产数据,这里用wait/notify机制来实现。

         详细的实现代码如下:

    1. class Info{ // 定义信息类  
    2.     private String name = "name";//定义name属性,为了与下面set的name属性区别开  
    3.     private String content = "content" ;// 定义content属性,为了与下面set的content属性区别开  
    4.     private boolean flag = true ;   // 设置标志位,初始时先生产  
    5.     public synchronized void set(String name,String content){  
    6.         while(!flag){  
    7.             try{  
    8.                 super.wait() ;  
    9.             }catch(InterruptedException e){  
    10.                 e.printStackTrace() ;  
    11.             }  
    12.         }  
    13.         this.setName(name) ;    // 设置名称  
    14.         try{  
    15.             Thread.sleep(300) ;  
    16.         }catch(InterruptedException e){  
    17.             e.printStackTrace() ;  
    18.         }  
    19.         this.setContent(content) ;  // 设置内容  
    20.         flag  = false ; // 改变标志位,表示可以取走  
    21.         super.notify();  
    22.     }  
    23.     public synchronized void get(){  
    24.         while(flag){  
    25.             try{  
    26.                 super.wait() ;  
    27.             }catch(InterruptedException e){  
    28.                 e.printStackTrace() ;  
    29.             }  
    30.         }  
    31.         try{  
    32.             Thread.sleep(300) ;  
    33.         }catch(InterruptedException e){  
    34.             e.printStackTrace() ;  
    35.         }  
    36.         System.out.println(this.getName() +   
    37.             " --> " + this.getContent()) ;  
    38.         flag  = true ;  // 改变标志位,表示可以生产  
    39.         super.notify();  
    40.     }  
    41.     public void setName(String name){  
    42.         this.name = name ;  
    43.     }  
    44.     public void setContent(String content){  
    45.         this.content = content ;  
    46.     }  
    47.     public String getName(){  
    48.         return this.name ;  
    49.     }  
    50.     public String getContent(){  
    51.         return this.content ;  
    52.     }  
    53. }  
    54. class Producer implements Runnable{ // 通过Runnable实现多线程  
    55.     private Info info = null ;      // 保存Info引用  
    56.     public Producer(Info info){  
    57.         this.info = info ;  
    58.     }  
    59.     public void run(){  
    60.         boolean flag = true ;   // 定义标记位  
    61.         for(int i=0;i<10;i++){  
    62.             if(flag){  
    63.                 this.info.set("姓名--1","内容--1") ;    // 设置名称  
    64.                 flag = false ;  
    65.             }else{  
    66.                 this.info.set("姓名--2","内容--2") ;    // 设置名称  
    67.                 flag = true ;  
    68.             }  
    69.         }  
    70.     }  
    71. }  
    72. class Consumer implements Runnable{  
    73.     private Info info = null ;  
    74.     public Consumer(Info info){  
    75.         this.info = info ;  
    76.     }  
    77.     public void run(){  
    78.         for(int i=0;i<10;i++){  
    79.             this.info.get() ;  
    80.         }  
    81.     }  
    82. }  
    83. public class ThreadCaseDemo03{  
    84.     public static void main(String args[]){  
    85.         Info info = new Info(); // 实例化Info对象  
    86.         Producer pro = new Producer(info) ; // 生产者  
    87.         Consumer con = new Consumer(info) ; // 消费者  
    88.         new Thread(pro).start() ;  
    89.         //启动了生产者线程后,再启动消费者线程  
    90.         try{  
    91.             Thread.sleep(500) ;  
    92.         }catch(InterruptedException e){  
    93.             e.printStackTrace() ;  
    94.         }  
    95.   
    96.         new Thread(con).start() ;  
    97.     }  
    98. }  

         执行结果如下:

     

         另外,在run方法中,二者循环的次数要相同,否则,当一方的循环结束时,另一方的循环依然继续,它会阻塞在wait()方法处,而等不到对方的notify通知。

  • 相关阅读:
    Shell学习(八)——dd命令
    PLSQL导出oracle表结构和数据
    如何让Linux 机器CPU使用率变高
    WebService学习总览
    Java发HTTP POST请求(内容为xml格式)
    Oracle—回车、换行符
    Oracle—merge into语法
    Apache解析和绑定域名
    MySQL根据经纬度按距离排序
    jquery监听input
  • 原文地址:https://www.cnblogs.com/web424/p/6807705.html
Copyright © 2020-2023  润新知