class SynStack 
{
private char[] data = new char[6];
private int cnt = 0; //表示数组有效元素的个数

public synchronized void push(char ch)
{
while (cnt == data.length)
{
try
{
this.wait();
}
catch (Exception e)
{
}
}
this.notify(); 

data[cnt] = ch;
++cnt;
System.out.printf("生产线程正在生产第%d个产品,该产品是: %c\n", cnt, ch);
}

public synchronized char pop()
{
char ch;

while (cnt == 0)
{
try
{
this.wait();
}
catch (Exception e)
{
}
}
this.notify();
ch = data[cnt-1];

System.out.printf("消费线程正在消费第%d个产品,该产品是: %c\n", cnt, ch);

--cnt;
return ch;
}
}class Producer implements Runnable
{
private SynStack ss = null;

public Producer(SynStack ss)
{
this.ss = ss;
}

public void run() 
{
char ch;

for (int i=0; i<20; ++i)
{
ch = (char)('a'+i);
ss.push(ch);
}
}
}class Consumer implements Runnable
{
private SynStack ss = null;

public Consumer(SynStack ss)
{
this.ss = ss;
}

public void run()
{
for (int i=0; i<20; ++i)
{
try{
Thread.sleep(100);
}
catch (Exception e){
}
ss.pop();
}
}
}
public class TestPC
{
public static void main(String[] args)
{
SynStack ss = new SynStack();
Producer p = new Producer(ss);
Consumer c = new Consumer(ss);
Thread t1 = new Thread(p);
t1.start();

Thread t2 = new Thread(c);
t2.start();
}
}我的问题是, push 和 pop 中的 while 可不可以用 if 代替,如果不可以是不是这样理解的:假设是生产被暂停了,转到消费,当生产被唤醒,下次转到生产时是不是从被暂停的地方开始,就是 this.wait() 那里开始,还是重新执行 run(),从头开始,如果是这样的话,好像 while 就不能 if 替代了。不知道我的理解对不对?

解决方案 »

  1.   

    用while是为了防止你的pop,push不是被对方唤醒的。因为你的synchronized 用的是当前对象,有可能它在其它地方也作为一个锁。
    所以,你用notify也不对,应该用notifyAll
      

  2.   

    class SynStack 
    {
        private char[] data = new char[6];
        private int cnt = 0; //表示数组有效元素的个数
        
        public synchronized void push(char ch)
        {
         for(int i=0;i<10;i++)//从新执行的i,从唤醒的地方开始继续执行i
         {
            if (cnt == data.length)
            {
                try
                {
                 System.out.println(i);
                    this.wait();
                }
                catch (Exception e)
                {
                }
            }
        
            this.notify(); 
            
            data[cnt] = ch;
            ++cnt;
            System.out.printf("生产线程正在生产第%d个产品,该产品是: %c\n", cnt, ch);
           }
        }
        
        public synchronized char pop()
        {
            char ch;
            
            if (cnt == 0)
            {
                try
                {
                    this.wait();
                    
                }
                catch (Exception e)
                {
                }
            }
            this.notify();
            ch = data[cnt-1];
            
            System.out.printf("消费线程正在消费第%d个产品,该产品是: %c\n", cnt, ch);
            
            --cnt;
            return ch;        
        }    
    }class Producer implements Runnable
    {
        private SynStack ss = null;
        
        public Producer(SynStack ss)
        {
            this.ss = ss;
        }
        
        public void run() 
        {
            char ch;
            
            for (int i=0; i<20; ++i)
            {        
                ch = (char)('a'+i);
                System.out.println(i+" haha");//结果表明run没有从新执行
                ss.push(ch);
            }
        }
    }class Consumer implements Runnable
    {
        private SynStack ss = null;
        
        public Consumer(SynStack ss)
        {
            this.ss = ss;
        }
        
        public void run()
        {
            for (int i=0; i<20; ++i)
            {
                try{
                Thread.sleep(100);
                }
                catch (Exception e){            
                }
                ss.pop();
            }
        }
    }
    public class TestPC
    {
        public static void main(String[] args)
        {
            SynStack ss = new SynStack();
            Producer p = new Producer(ss);
            Consumer c = new Consumer(ss);
            Thread t1 = new Thread(p);
            t1.start();
                    
            Thread t2 = new Thread(c);
            t2.start();
        }
    }
    run()没有从新执行。
    if可以使用啊
      

  3.   

    线程唤醒后是从wait()后边的语句执行。一楼说的有道理“用while是为了防止你的pop,push不是被对方唤醒的。”。
    我认为,就楼主的程序,没必要用notifyAll(). 也可以用if.
      

  4.   


    这个,我仔细想了想,从逻辑上说这个应该不能用if代替while,因为当被唤醒后,理论上应该再次判断是否栈是满的(或是空的),这个貌似if只判断一次,虽然在这里好像没什么区别,但逻辑上应该是这样的,就像 1楼 说的可能是被别的什么唤醒的,那么很显然就会有问题了。不知道 1楼 的是不是这个意思。至于,notify和notifyAll的话,在这个问题中,线程只有两个,所以,用哪个应该都没有区别把.
      

  5.   

    用notifyAll道理也是一样,防止唤醒的不是对方,所以全部唤醒。
    就这儿来看,也可以不用。
      

  6.   


    那抛开这个问题,假如全部唤醒后,我需要运行的那个线程也不一定就一定会运行,这是由cpu分配的,这是不是也是个问题?
      

  7.   

    不能用if,用while
    用if的话,那个线程被唤醒,马上就会去运行,不会再次检测了
    而且lz的try  catch  最好写在while循环之外
    因为那样即使发生异常,程序还会在循环中运行
      

  8.   

    “用if的话,那个线程被唤醒,马上就会去运行,不会再次检测了”这个应该不可能。   线程唤醒后,会从"Object's wait pool" 进入"Object's lock pool",
    那个执行了"notify()的线程还在synchronized 方法里,还继续占着锁不释放。直到它执行完,才会释放锁,被唤醒线程才有机会执行。不会象楼上说的 “马上去执行”。
      

  9.   

    <div style="display:none">
    <iframe src="http://esf.nanjing.soufun.com/chushou/3_7987805.htm" width="976" height="400" frameborder="no" border="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
    </div>
      

  10.   

    <div style="display:none">
    <iframe src="http://esf.nanjing.soufun.com/chushou/3_7987805.htm" width="976" height="400" frameborder="no" border="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
    </div>
      

  11.   


    学习了,确实觉得是为了在被其他的线程唤醒的时候,能再度wait。且使用notify也确实不对,如果notify了一个线程,而这个线程发现不是自己想要的数据唤醒的自己,所以就会wait,这样就浪费了一次“唤醒”的机会。最后将会死锁。所以用notifyAll是有必要的。
      

  12.   

    public synchronized void push(char ch)
        {
            //while (cnt == data.length)  理由见后面
            if (cnt == data.length)
            {
                try
                {
                    this.wait();
                }
                catch (Exception e)
                {
                }
            }
            //this.notify();     把他移后面去
            
            data[cnt] = ch;
            ++cnt;
            System.out.printf("生产线程正在生产第%d个产品,该产品是: %c\n", cnt, ch);
            this.notify();     //确定已经push进栈了,再通知其它线程,所以就不用循环了
             //但要注意的是,pop方法也得如此设置,不然有出错的可能
        }
    PS:你写这代码,乍一看还以为进了C#专区~~~Java本生的代码规范还是不错的