生产者与消费者问题解决:解决先打印出消费的情况

Posted 黑夜不再来

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了生产者与消费者问题解决:解决先打印出消费的情况相关的知识,希望对你有一定的参考价值。

有bug 的代码(马士兵老师讲解的):

  1 <span style="font-size:14px;">/**生产者消费者问题,涉及到几个类 
  2  * 第一,这个问题本身就是一个类,即主类 
  3  * 第二,既然是生产者、消费者,那么生产者类和消费者类就是必须的 
  4  * 第三,生产什么,消费什么,所以物品类是必须的,这里是馒头类 
  5  * 第四,既然是线程,那么就不是一对一的,也就是说不是生产一个消费一个,既然这样,多生产的往哪里放, 
  6  *      现实中就是筐了,在计算机中也就是数据结构,筐在数据结构中最形象的就是栈了,因此还要一个栈类 
  7  */  
  8 package thread;  
  9   
 10 public class ProduceConsume {  
 11   
 12     public static void main(String[] args) {  
 13         SyncStack ss = new SyncStack();//建造一个装馒头的框  
 14         Producer p = new Producer(ss);//新建一个生产者,使之持有框  
 15         Consume c = new Consume(ss);//新建一个消费者,使之持有同一个框  
 16         Thread tp = new Thread(p);//新建一个生产者线程  
 17         Thread tc = new Thread(c);//新建一个消费者线程  
 18         tp.start();//启动生产者线程  
 19         tc.start();//启动消费者线程  
 20     }  
 21   
 22 }  
 23   
 24 //馒头类  
 25 class SteamBread{  
 26     int id;//馒头编号  
 27     SteamBread(int id){  
 28         this.id = id;  
 29     }  
 30     public String toString(){  
 31         return "steamBread:"+id;  
 32     }  
 33 }  
 34   
 35 //装馒头的框,栈结构  
 36 class SyncStack{  
 37     int index = 0;  
 38     SteamBread[] stb = new SteamBread[6];//构造馒头数组,相当于馒头筐,容量是6  
 39       
 40     //放入框中,相当于入栈  
 41     public synchronized void push(SteamBread sb){  
 42         while(index==stb.length){//筐满了,即栈满,  
 43             try {  
 44                 this.wait();//让当前线程等待  
 45             } catch (InterruptedException e) {  
 46                 // TODO Auto-generated catch block  
 47                 e.printStackTrace();  
 48             }  
 49         }  
 50         this.notify();//唤醒在此对象监视器上等待的单个线程,当前对象为syncstack
 51         stb[index] = sb;  
 52         this.index++;  
 53     }  
 54       
 55     //从框中拿出,相当于出栈  
 56     public synchronized SteamBread pop(){  
 57         while(index==0){//筐空了,即栈空  
 58             try {  
 59                 this.wait();  
 60             } catch (InterruptedException e) {  
 61                 // TODO Auto-generated catch block  
 62                 e.printStackTrace();  
 63             }  
 64         }  
 65         this.notify();  
 66         this.index--;//push第n个之后,this.index++,使栈顶为n+1,故return之前要减一  
 67         return stb[index];  
 68     }  
 69 }  
 70   
 71 //生产者类,实现了Runnable接口,以便于构造生产者线程  
 72 class Producer implements Runnable{  
 73     SyncStack ss = null;  
 74     Producer(SyncStack ss){  
 75         this.ss = ss;  
 76     }  
 77     @Override  
 78     public void run() {  
 79         // 开始生产馒头  
 80         for(int i=0;i<20;i++){  
 81             SteamBread stb = new SteamBread(i);  
 82             ss.push(stb);  
 83             System.out.println("生产了"+stb);  
 84             try {  
 85                 Thread.sleep(10);//每生产一个馒头,睡觉10毫秒  
 86             } catch (InterruptedException e) {  
 87                 // TODO Auto-generated catch block  
 88                 e.printStackTrace();  
 89             }  
 90         }  
 91     }  
 92 }  
 93   
 94 //消费者类,实现了Runnable接口,以便于构造消费者线程  
 95 class Consume implements Runnable{  
 96     SyncStack ss = null;  
 97     public Consume(SyncStack ss) {  
 98         super();  
 99         this.ss = ss;  
100     }  
101     @Override  
102     public void run() {  
103         // TODO Auto-generated method stub  
104         for(int i=0;i<20;i++){//开始消费馒头  
105             SteamBread stb = ss.pop();  
106             System.out.println("消费了"+stb);  
107             try {  
108                 Thread.sleep(100);//每消费一个馒头,睡觉100毫秒。即生产多个,消费一个  
109             } catch (InterruptedException e) {  
110                 // TODO Auto-generated catch block  
111                 e.printStackTrace();  
112             }  
113         }  
114     }     
115 }</span>  

解决方法:

http://blog.csdn.net/u013243986/article/details/48755183

看过 http://blog.csdn.net/thinkpadshi/article/details/8163751     下面的评论说:    感觉你的代码有问题啊,两个run()方法里面的打印语句的执行先后问题,假设开始在消费时index==0;这时wait()了,生产者便抢到锁,index+1;同时叫醒消费者,这个时候要是消费者先于生产者的打印了一条消费了0个,之后再打印生产了0个怎么办??!,我执行后也发现这样的问题,如果把100毫秒改为10毫秒,会有更多这样的情况产生.

于是自己改了下博主的代码:

  1 package deadLockThread;  
  2   
  3 public class SX {  
  4     public static void main(String[] args) {  
  5         ProductList pl = new ProductList();  
  6         Factory f = new Factory(pl);  
  7         Consumer c = new Consumer(pl);  
  8   
  9         Thread t1 = new Thread(f);  
 10         Thread t2 = new Thread(c);  
 11   
 12         t1.start();  
 13         t2.start();  
 14   
 15     }  
 16 }  
 17   
 18 class Product {  
 19     private int id;  
 20   
 21     Product(int id) {  
 22         this.id = id;  
 23     }  
 24   
 25     @Override  
 26     public String toString() {  
 27         return "Product [id=" + id + "]";  
 28     }  
 29   
 30 }  
 31   
 32 class ProductList {  
 33     int index = 0;  
 34     private Product[] p = new Product[6];  
 35   
 36     public synchronized void push(Product pr) {  
 37   
 38         while (index == p.length) {  
 39             try {  
 40                 this.wait();  
 41             } catch (InterruptedException e) {  
 42                 // TODO Auto-generated catch block  
 43                 e.printStackTrace();  
 44             }  
 45         }  
 46         this.notify();  
 47         p[index] = pr;  
 48         <span style="color:#ff0000;">System.out.println("生产了" + p[index]);</span>  
 49         index++;  
 50   
 51     }  
 52   
 53     public synchronized Product pop() {  
 54   
 55         while (index == 0) {  
 56             try {  
 57                 this.wait();  
 58             } catch (InterruptedException e) {  
 59                 // TODO Auto-generated catch block  
 60                 e.printStackTrace();  
 61             }  
 62         }  
 63         this.notify();  
 64         index--;  
 65         <span style="color:#ff0000;">System.out.println("消费了" + p[index]);</span>  
 66         return p[index];  
 67   
 68     }  
 69 }  
 70   
 71 class Factory implements Runnable {  
 72   
 73     private ProductList pl = null;  
 74   
 75     Factory(ProductList pl) {  
 76         this.pl = pl;  
 77     }  
 78   
 79     @Override  
 80     public void run() {  
 81         // TODO Auto-generated method stub  
 82         for (int i = 0; i < 20; i++) {  
 83             Product p = new Product(i);  
 84             pl.push(p);  
 85             try {  
 86                 Thread.sleep(10);  
 87             } catch (InterruptedException e) {  
 88                 // TODO Auto-generated catch block  
 89                 e.printStackTrace();  
 90             }  
 91         }  
 92   
 93     }  
 94 }  
 95   
 96 class Consumer implements Runnable {  
 97     private ProductList pl = null;  
 98   
 99     Consumer(ProductList pl) {  
100         this.pl = pl;  
101     }  
102   
103     @Override  
104     public void run() {  
105         // TODO Auto-generated method stub  
106         for (int i = 0; i < 20; i++) {  
107             Product p = pl.pop();  
108             try {  
109                 Thread.sleep(10);  
110             } catch (InterruptedException e) {  
111                 // TODO Auto-generated catch block  
112                 e.printStackTrace();  
113             }  
114         }  
115   
116     }  
117 }

我就只是改动了  输出的位置 这是由于System.out.println()函数不是立刻就打印的,它要调用其他的函数来完成输出,所以System.out.println("生产了" + p[index]);  还有 System.out.println("消费了" + p[index]);具体的打印时间就不确定了.   但是如果把输出打印放在同步代码块里面,就不会有这样的问题.因为会完成打印之后才会执行下一步.

以上是关于生产者与消费者问题解决:解决先打印出消费的情况的主要内容,如果未能解决你的问题,请参考以下文章

利用jvisualvm.exe搞一个关于生产者消费者的另一些纠结的问题

多线程操作实例——生产者与消费者

在windows操作系统中用信号量机制解决生产者消费者问题的代码

java实现生产者消费者问题

Linux系统编程5_条件变量与互斥锁

Linux系统编程5_条件变量与互斥锁