public class TestProducerConsumer{
public static void main(String args[]){
StackBasket sb=new StackBasket();
Producer pro=new Producer(sb);
Consumer con=new Consumer(sb);
Thread th=new Thread(pro);
Thread th2=new Thread(con);
th.start();
th2.start();
}
}class Bread{
static int id;
Bread(){
id++;
}
public String toString() {
return "WoTou : " + id;
}
}
class StackBasket{
Bread arr[]=new Bread[6];
int index=0;

public synchronized void push(Bread bread ){
while(index==arr.length){
try{
  this.wait();
}catch(InterruptedException e){
e.printStackTrace();
}
}
this.notify();
arr[index]=bread;
index++;


}

public synchronized Bread pop(){
while(index==0){
try{
this.wait();
}catch(InterruptedException e ){
e.printStackTrace();
}
}
this.notify();
index--;
return arr[index];
}
}class Producer implements Runnable {
Bread bread=new Bread();
StackBasket sb;
Producer(StackBasket _sb ){
sb=_sb;
}
public void run(){
for(int i=0;i<20;i++){
Bread bread=new Bread();
sb.push(bread);
System.out.println("生产了"+bread);
}
}
}class Consumer implements Runnable {
StackBasket sb;
Consumer(StackBasket _sb){
sb=_sb;
}
public void run(){
for(int i=0;i<20;i++){
Bread bread=sb.pop();
System.out.println("消费了"+bread);
}
}
}
这段代码差不多是马士兵java se中的 ,我不明白的是,wait()方法后是谁调用notify()唤醒它,是它自身吗?还是别的执行到synchronized同步语句的线程.不知道自己这么讲,大家明不明白.简单点就是上面都是只有一个Producer 和 Consumer 线程的,他们中的任何一个wait()了,久好像不存在还有别的线程来notify()他们了,这到底是怎么回事?谢谢大家了,我在线等着

解决方案 »

  1.   

    wait, notify, synchronized 这些网上太多资料了。楼主可以去搜下。
    http://www.javaeye.com/topic/806990http://www.javaeye.com/topic/808550
    表示下没白来,推荐给你两个资料,不错。
      

  2.   

    wait()和notify()都是属于Object类的,也就是说每个对象都会有这些方法。当一个线程在获取StackBasket对象资源失败的时候,这个线程就会调用该StackBasket对象的wait()方法,进入该对象的等待队列里面(每个对象都有默认的锁和等待队列);而当StackBasket对象有资源可用的时候,就会调用StackBasket对象的notify()或者notifyAll()来唤醒该对象等待队列里面的线程。
    ---------------------------
      

  3.   

    对于这个问题,说实话我也比较菜,下面我只是说说我的愚见,呵呵1、先看main方法里面的几句代码。//这句代码创建一个面包仓库,通过楼主贴出的代码,我们知道它最多可以放6个面包。
    StackBasket sb=new StackBasket();
    //这句话当然是创建生产者了,呵呵,但要注意的是它传入了一个sb对象(估且叫它傻比对象吧,哈)
            Producer pro=new Producer(sb);
    //同样这句话创建了消费者,还是传入了sb对象,到这我们知道了,生产者和消费者共享一个sb对象
            Consumer con=new Consumer(sb);
    //下面两句代码创建两个线程,这个我就不费话了。
            Thread th=new Thread(pro);
            Thread th2=new Thread(con);
    //启动生产者线程,它的任务是往库里放面包。它调用的是sb对象的push方法,但这个线程要执行这个方法是有条件的,那就是它必须先拥有sb对象的锁。
            th.start();
    //启动生产者线程,它的任务是从库里拿面包。它调用的是sb对象的pop方法,同理这个线程要执行这个方法也要先拥有sb对象的锁。
            th2.start();好了,进入正题,来分析wait和notify方法的用处。先看生产者线程,执行的push方法,首先是while循环,我们看源码
     while(index==arr.length){
                try{
                  this.wait();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
    因为程序先启动的生产者,如果不出什么意外的话,生产者会执行到while里面,也就是index==arr.length为true,当然这个是不确定的。因为程序是有很大的概率这么执行的,所以我假设可以进入while,也以这个情况来分析执行原理。这个while可以保证,如果面包生产满了,不会去执行while后面的代码,这个我们可以不用太关注,最主要的是关注wait()方法在这里调用了,它的意思就是,面包我生产好了,我该去休息了,如果消费者把面包拿走了请通知我(notify)。到这里,我们知道了,生产者要停下来,让消费者执行。下面来看看消费者,因为生产者只有等到消费者的通知才会生产,所以消费者它必然会执行,我们看消费者的源码
    //根据我的假设,消费者第一次执行这个方法时是不会进入while的(因为概率的问题,我们分析时必须假设,呵呵)
    while(index==0){
                try{
                    this.wait();
                }catch(InterruptedException e ){
                    e.printStackTrace();
                }
            }
    //你看,这消费者多贪,刚来就通知生产者准备生产(为啥这么说呢,因为这是同步方法,消费者虽然通知了生产者,但是消费者还没有执行这个方法是不会释放对象sb的锁的,它只是告诉生产者,你可以来拿sb的锁进行生产,但要等我执行完方法你才有机会,当然可能消费者执行完这个方法就会轮到生产者,也有可能不行,因为线程的时间片长度我们是不能保证的。)要注意的是,代码里的this正是sb对象,为什么,思考下就明白了,同理生产者执行的push方法里的this也是sb对象,理解这点是相当重要的。请一定要理解。
            this.notify();
            index--;
            return arr[index];
    消费者方法执行完了,生产者可能会得到sb对象的锁,从而得到执行的权力,但也有可能是消费者的时间片还没有结束,还会继续占着sb的锁,继续执行,那么生产者只有等待,我再作一个假设,消费者能执行到index=0的情况(作这个假设是因为我想完整的分析下这里的原理),消费者一旦进入while,说明没有面包了,所以让它时入while,然后会调用wait方法,当他调了wait方法,他就会释放sb的锁,失去执行的机会,因为消费者在wait之前每次都通知了生产者,生产者处理激知状态,可以得到sb的锁,我们知道他一定能得到,因为没有人和它抢了,呵呵。大休上就是这原理,我也说下这里用到的两个知识点,第一,为什么说两个线程一定要得到sb的锁才能执行呢,这是因为push和pop都是同步方法,他们者要获得this的锁也就是sb的锁对于此程序来说,才能执行。
    第二,wait和notify两个方法必须是同步方法或同步代码块的锁对象,而且调用wait 方法后当前线程会释放锁,并等到这个对象调用notify方法通知他啥时开始可以尝试得到锁。而调用notify方法的线程他不会立即失去锁,也就是不会立即停下来。
    头一次回贴写这么多字,但愿楼主能看懂一点。
      

  4.   

    补充一下,如果用下面的代码测试,可能会更清析点,其实没改啥,就是把6改成了1
    Bread arr[]=new Bread[1];
      

  5.   

    1 回答问题.当然是consumer wait()了,producer notify() consumer,反之亦然,这里只有两个谈不上notifyAll()
    2 正确理解从j2se 5.0开始的线程状态. 一个线程处在Runnable状态才有可能获得cup的时间片.当consumer执行到
       Bread bread=sb.pop()时1)如果还没进入到pop()方法(获得sb的锁),cpu时间片即被抢,那么执行producer 线程         2)如果进入到pop()方法后在还没离开pop()之前,cpu时间片被抢,恰好produer线程获得了时间片,那么当它执行到  sb.push(bread)时由于等待sb的锁而进入到线程的blocked状态,也就是说,如果没有其他线程调用sb.notify()或sb.notifyAll(),它将永远不会进入到Runnable状态
    3  这个题目是StackBasket类自同步,这个最好办,外面调用多线程不用考虑多线程的问题 .只考虑pop,和push方法就可以了.
    4 1)先测试条件,如果不满足先wait(),如果满足,执行关键代码,然后notify  .2)wait时注意要把Wait()放在一个测试条件的while循环中.
    5 正确理解,为什么要在循环中wait(),这是因为,就如同本例,如果有两个消费者,一个消费者测试当数组不空时才消费,当空了,它等待,这时生产者被唤醒,执行后数组有一个元素,生产者在数组引用上调用notifyAll,两个消费同时被从blocked状态唤醒,进入Runnable状态,这时如果恰好另外一个消费者先抢到了时间片并把生产的那一个消费了.然后它继续wait这样问题 来了,本消费者线程由于没有处在while的wait中,它会往下执行,去消费,可是面包数组已经空了...这样就出问题 了
    6 换一个形象的说法. 假设有两个爱吃的人A,B都去等盘里的面包,当盘子里为空时,它们都等待.这时过了一个小时,盘子里终于出现了一个面包,两个人同时被唤醒,A运气好先抢到,吃了面包.B如果不是处于带循环的wait,它还是向盘子要面包,但是盘子已经空了,这样B会把盘子吃了吗?这是问题 也是bug