生产者—消费者模型
生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据。
这里实现如下情况的生产--消费模型:
生产者不断交替地生产两组数据“姓名--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通知