CSDN-CSDN社区-Java-Java SE

收藏 [推荐] 生产者与消费者(线程问题)[问题点数:20]

  • hong724
  • ( H)
  • 等 级:
  • 结帖率:
楼主发表于:2009-06-03 23:37:38
public class TestProducerConsumer {
public static void main(String[] args) {
// TODO Auto-generated method stub

SyncStack ss=new SyncStack();
producer p=new producer(ss);
customer c=new customer(ss);
new Thread(p).start();
new Thread(c).start();
}
}

class WoTou{
int id=0;
WoTou(int id){
this.id=id;
}
public String toString(){
return "WoTou"+id;
}
}

class SyncStack{
int index=0;
WoTou [] ArrWT=new WoTou[6];
public synchronized void push(WoTou wt){
if(index>=ArrWT.length){
try{
System.out.println("满了,等待消费");
wait();
}catch(InterruptedException e){
e.printStackTrace();
}

}
notify();
ArrWT[index]=wt;
index++;
}

public synchronized WoTou pop(){
if(index==0){
try{
System.out.println("没WoTou了,等待生产");
wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
notify();
index--;
return ArrWT[index];

}
}

class producer implements Runnable{
SyncStack ss=null;
producer(SyncStack ss){
this.ss=ss;
}

public void run(){
for (int i =0;i <20;i++){
WoTou wt=new WoTou(i);
ss.push(wt);
System.out.println("生产了:"+wt);
try{
Thread.sleep(200);
}catch(Exception e){
e.printStackTrace();
}
}
}

}

class customer implements Runnable{
SyncStack ss=null;
customer(SyncStack ss){
this.ss=ss;
}

public void run(){
for(int i=0;i <20;i++){
WoTou wt=ss.pop();
System.out.println("消费了:        "+wt);
try{
Thread.sleep(1000);
}catch(Exception e){
e.printStackTrace();
}
}

}

}


得出的结果为:
生产了:WoTou0
消费了:        WoTou0
生产了:WoTou1
生产了:WoTou2
生产了:WoTou3
生产了:WoTou4
消费了:        WoTou4
生产了:WoTou5
生产了:WoTou6
生产了:WoTou7
满了,等待消费
生产了:WoTou8
消费了:        WoTou7
满了,等待消费
消费了:        WoTou8
生产了:WoTou9
满了,等待消费
生产了:WoTou10
消费了:        WoTou9
满了,等待消费
生产了:WoTou11
消费了:        WoTou10
满了,等待消费
消费了:        WoTou11
生产了:WoTou12
满了,等待消费
生产了:WoTou13
消费了:        WoTou12
满了,等待消费
生产了:WoTou14
消费了:        WoTou13
满了,等待消费
消费了:        WoTou14
生产了:WoTou15
满了,等待消费
生产了:WoTou16
消费了:        WoTou15
满了,等待消费
生产了:WoTou17
消费了:        WoTou16
满了,等待消费
消费了:        WoTou17
生产了:WoTou18
满了,等待消费
生产了:WoTou19
消费了:        WoTou18
消费了:        WoTou19
消费了:        WoTou6
消费了:        WoTou5
消费了:        WoTou3
消费了:        WoTou2
消费了:        WoTou1


提示满了,怎么还在生产呢?难道是wait()没起作用?

请教高手。
回复次数:35
#1楼 得分:0回复于:2009-06-04 01:01:29
生产和消费的打印语句放到生产和消费的方法里
Java code
public synchronized void push(WoTou wt) { if (index >= ArrWT.length) { try { System.out.println("满了,等待消费"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } ArrWT[index++] = wt; System.out.println("生产了:" + wt); notify(); } public synchronized WoTou pop() { if (index == 0) { try { System.out.println("没WoTou了,等待生产"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费了: " + ArrWT[--index]); notify(); return ArrWT[index]; }
#2楼 得分:0回复于:2009-06-04 01:01:36
生产和消费的打印语句放到生产和消费的方法里
Java code
public synchronized void push(WoTou wt) { if (index >= ArrWT.length) { try { System.out.println("满了,等待消费"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } ArrWT[index++] = wt; System.out.println("生产了:" + wt); notify(); } public synchronized WoTou pop() { if (index == 0) { try { System.out.println("没WoTou了,等待生产"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("消费了: " + ArrWT[--index]); notify(); return ArrWT[index]; }
#3楼 得分:0回复于:2009-06-04 09:47:25
楼主,你这个程序生产满了是因为在SyncStack类里,规定生产满6个,就认为满了,所以等待。
等到消费了,它的数量就少于6个了。然后它就又生产。直到生产producer类里规定的数量(20)为止.
  • Yedy2000用户头像
  • Yedy2000
  • (要做得更好)
  • 等 级:
#4楼 得分:0回复于:2009-06-04 10:05:39
引用 3 楼 wangljgood 的回复:
楼主,你这个程序生产满了是因为在SyncStack类里,规定生产满6个,就认为满了,所以等待。
等到消费了,它的数量就少于6个了。然后它就又生产。直到生产producer类里规定的数量(20)为止.


正解
#5楼 得分:0回复于:2009-06-05 15:49:25
但是当它满了之后,应该是先要消费了, 可是为什么它又会生产?
生产了:WoTou7
满了,等待消费
生产了:WoTou8
消费了:        WoTou7
#6楼 得分:0回复于:2009-06-05 19:41:52
pop()方法中的
notify();
index--; 这可以导致去生产!
return ArrWT[index];
当执行完这三句代码后,退出了方法,就说明c释放了对象的锁:p线程已经醒来,就会和c线程抢占资源,
此时customer中的
System.out.println("消费了:        "+wt); 代码还没有执行。
这个时候就要看p和c线程那个能抢到资源了。
出现你这种情况是p抢占到了资源了,因为允许生产,所以就生产了。
然后p生产满了,然后c线程接着执行它的代码,所以你会看到这样的情况。
修改了一下lz的代码
Java code
,lz好好揣摩一下! public class TestProducerConsumer { public static void main(String[] args) { SyncStack ss = new SyncStack(); producer p = new producer(ss); customer c = new customer(ss); new Thread(p).start(); new Thread(c).start(); } } class WoTou { int id = 0; WoTou(int id) { this.id = id; } public String toString() { return "WoTou" + id; } } class SyncStack { int index = 0; WoTou[] ArrWT = new WoTou[6]; public synchronized void push(WoTou wt) { if (index >= ArrWT.length) { try { System.out.println("---------------满了,等待消费"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } ArrWT[index] = wt; index++; notify();//执行完自己的代码才去唤醒别人,要不别人就会和你抢cpu资源。 } // 改变这无返回值 public synchronized void pop() { if (index == 0) { try { System.out.println("没WoTou了,等待生产"); wait(); } catch (InterruptedException e) { e.printStackTrace(); } } notify();//虽然我唤醒了那些等待的线程,但线程我还是持有对象的锁!!! index--; // return ArrWT[index]; // 改变如下: System.out.println("消费了: " + ArrWT[index]); } } class producer implements Runnable { SyncStack ss = null; producer(SyncStack ss) { this.ss = ss; } public void run() { for (int i = 0; i < 20; i++) { WoTou wt = new WoTou(i); ss.push(wt); System.out.println("生产了:" + wt); try { Thread.sleep(200); } catch (Exception e) { e.printStackTrace(); } } } } class customer implements Runnable { SyncStack ss = null; customer(SyncStack ss) { this.ss = ss; } public void run() { for (int i = 0; i < 20; i++) { // WoTou wt=ss.pop();//锁定了对象 //执行到下面这句,你已经没有对象的锁!!! // System.out.println("消费了: "+wt); // 改变如下: ss.pop();//锁定了对象 try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } } } }

关键还是持有对象的锁!!!
#7楼 得分:0回复于:2009-06-06 18:33:00
楼上分析的分析的非常正确,楼主的问题总结:
1.为什么满了还生产  :输出语句应该放在方法锁内,事实上已经消费了一个,但是由于输出语句在外面所以少打印一个
2.还有就是if (index >= ArrWT.length) {
  改为while(index >= ArrWT.length) ----避免出现异常时还能再次判断
#8楼 得分:0回复于:2009-06-06 21:43:39
我觉得是不是你的wait()不对?

是不是要加上在哪个资源上wait()?然后在对应的资源上notify()才会管用吧?
#9楼 得分:0回复于:2009-06-08 19:07:01
比较经典的问题,生产者和消费者问题就像链表和二叉树一样。
#10楼 得分:0回复于:2009-06-12 09:21:08
下面是我实现的一个生产者消费者问题,
LZ参考下:
Java code
/** * 生产者消费者问题 */ package net.csdn.blog.johnston; /** * @author johnston678 * * @version 2009-05-06 */ public class ProducerConsumer { /** * @param args */ public static void main(String[] args) { ProductBox pb = new ProductBox(); Producer p = new Producer(pb); Consumer c = new Consumer(pb); Thread pThread = new Thread(p); Thread cThread = new Thread(c); pThread.setPriority(Thread.MAX_PRIORITY); pThread.start(); cThread.start(); } } /** * 产品对象 * @author johsnton678 */ class Product { int id; public Product(int id) { super(); this.id = id; } public String toString(){ return "Product:" + id; } } /** * 产品盒对象 * @author johnston678 */ class ProductBox { Product[] productbox = new Product[6]; int index = 0; public ProductBox() { super(); } public synchronized void push(Product p) { while (index == productbox.length) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.notify(); productbox[index] = p; index ++; } public synchronized Product pop() { while (index == 0) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } this.notify(); index --; return productbox[index]; } } /** * 生产者 * @author johnston678 */ class Producer implements Runnable { ProductBox productbox = null; public Producer(ProductBox productbox) { super(); this.productbox = productbox; } @Override public void run() { for (int i=0; i<10; i++) { Product p = new Product(i); productbox.push(p); System.out.println("produce:" + p); try { Thread.sleep((int)(Math.random() * 200)); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * 消费者 * @author johnston678 */ class Consumer implements Runnable { ProductBox productbox = null; public Consumer(ProductBox productbox) { super(); this.productbox = productbox; } @Override public void run() { for (int i=0; i<10; i++) { Product p = productbox.pop(); System.out.println("consume:" + p); try { Thread.sleep((int)(Math.random() * 1000)); } catch (InterruptedException e) { e.printStackTrace(); } } } }


运行结果:
produce:Product:0
consume:Product:0
produce:Product:1
produce:Product:2
produce:Product:3
produce:Product:4
produce:Product:5
produce:Product:6
consume:Product:6
produce:Product:7
consume:Product:7
produce:Product:8
consume:Product:8
produce:Product:9
consume:Product:9
consume:Product:5
consume:Product:4
consume:Product:3
consume:Product:2
consume:Product:1
#11楼 得分:0回复于:2009-06-13 18:58:40
使用线程池,非常的easy,记得使用容量有限的任务队列就可以。
#12楼 得分:0回复于:2009-06-14 18:52:38
11楼如何使用线程池呢?

#13楼 得分:0回复于:2009-06-15 19:04:41
生产者,消费者的模型,baidu下,很多的例子,这种我认为学java的人都应该会的
#14楼 得分:0回复于:2009-06-16 02:26:35
楼主貌似是拿了 尚学堂的例子。
呵呵。
生产者消费者线程可以有多重写法。
有比这个更容易的。
呵呵。
#15楼 得分:0回复于:2009-06-29 15:29:21
是的啦,你有没有遇到这样的问题呀???
#16楼 得分:0回复于:2009-06-30 10:18:02
其实生产者与消费者就是为了让学线程的人理解用wait()和notify()来实现多线程的同步

其实有更好理解的例子,我觉的如果写出来,就说明能用了

就是建立两个线程,一个打印1,一个打印0,两个线程同时运作,就打印10101010101010.。。。。
#17楼 得分:0回复于:2009-07-01 23:04:50
我来回一下,基本就是wait()和notifyAll()两个方法。
思路是:一生产者一消费者一罐子
重点是罐子:
有两个同步方法取和存,取的时候发现没有可取就调用wait()阻塞(此时阻塞的是调用取方法的消费者线程)
存的时候发现罐子中的东西已经足够多,就调用wait()阻塞(此时阻塞的是调用存方法的生产者线程)
如果上述情况没有发生,则执行存和取,并在方法结束和return前调用notifyAll(),使所有阻塞线程重新转为竞争上岗状态。
调用wait()和notifyAll()方法时必须在当前线程上,否则会抛出一个运行时异常。
楼主的错误应该与线程无关,估计是判断环节出了漏子

#21楼 得分:0回复于:2009-07-02 06:15:06
学习了.
#22楼 得分:0回复于:2009-07-02 11:24:54
weiluo12
已作出了分析,synchronized 用的this作为锁对象,同一时候也只有一个线程能拿到锁,wait(),notify()都不是长占锁操作,所以当你在pop()中notify()即叫醒了其它线程,
那么当pop()运行完了,锁交出,push()运行的线程可能会抢到锁开始运行,那么在消费者里的System.out.println("消费了:        " + wt);就不会输出了......

不过生产者消费者模式使用已在jdk1.5的时候已提供了很好的支持LinkedBlockingQueue
#23楼 得分:0回复于:2009-07-02 12:09:30
ding
#24楼 得分:0回复于:2009-07-02 12:35:21
学习了
#25楼 得分:0回复于:2009-07-02 13:08:06
一看就知道是拿老马的例子来的..
#26楼 得分:0回复于:2009-07-02 18:50:46
LZ,你在二个wait和二个notify前面加上this试试,调试了一下,应该没问题了。
#27楼 得分:0回复于:2009-07-02 18:52:32
学习
#28楼 得分:0回复于:2009-07-03 02:22:44
向高手学习
#29楼 得分:0回复于:2009-07-03 07:53:06
needit thanks
  • lsl031用户头像
  • lsl031
  • (lsl031)
  • 等 级:
#30楼 得分:0回复于:2009-07-03 09:04:17
学习
#31楼 得分:0回复于:2009-07-03 09:18:08
学习
#32楼 得分:0回复于:2009-07-03 18:38:08
mark
#33楼 得分:0回复于:2009-07-05 17:38:57
不错。了解。了解!!!
#34楼 得分:0回复于:2009-07-07 13:27:04
是的了, 哈哈, 看了他的还觉得可以了,怎么??你也看过吗》??
#35楼 得分:0回复于:2009-07-09 10:11:45
支持6楼的解法