2016-08-19 25 views
4

所以我一直在一个简单的等待/在Java中,由于某种原因我一直没能得到它的正常运行通知的例子。如果有人能够看到可能是什么问题,将非常感谢!通知()和notifyAll的()没有在我的Java代码工作

class producer implements Runnable { 
    StringBuffer sb; 
    producer() { 
     sb=new StringBuffer(""); 
    } 

    public void run() { 
     synchronized(sb) { 
      for(int i = 0; i < 10; i++) { 
       try { 
        sb.append(i+" "); 
        System.out.println("Appending ... "); 
       } catch (Exception e) {} 
      } 
      sb.notify(); 
     } 
    } 
} 

class consumer implements Runnable { 
    producer p; 
    consumer(producer pp) { 
     this.p = pp; 
    } 

    public void run() { 
     System.out.println("Rreached"); 
     synchronized(p.sb) { 
      try { 
       p.sb.wait(); 
      } catch (Exception e) {} 
      System.out.println(p.sb); 
     } 
    } 
} 

class Thread_Comunication { 
    public static void main (String [] args) { 
     producer p = new producer(); 
     consumer c = new consumer(p); 

     Thread t1 = new Thread(p); 
     Thread t2 = new Thread(c); 
     t1.start(); 
     t2.start(); 
    } 
} 

输出:

Appending ... 
Rreached // randome Position 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 

所以出于某种原因线程t1没有醒t2还是我失去了别的东西完全?

+1

至少把'e.printStackTrace();'在'catch'块,否则你永远不会知道,如果发生异常。 – Jesper

+1

注意:Java语言约定是类名以大写字母开头(因此'producer'不是一个好的类名)。在类,方法或包名称中不使用下划线,只用于全部为大写的常量名称。类具有像“ThreadCommunication”这样的名称,而不是“Tread_Comunication”。还建议赋予变量有意义的名称,而不是'p'和'pp'。 – RealSkeptic

+0

关闭投票人:发布的代码确实重现了问题(至少尽可能地多,因为代码取决于可能或可能不会发生在调度程序的判断的竞争条件)。在发布的代码和显示的输出之间,找出这里发生的事情是很实际的。 –

回答

3

通知除非另一个线程在等待什么都不做。你的代码完全依赖于通知(它需要一个条件变量),并依赖于生产者之前运行的消费者才能工作。

根据你的输出碰巧生产者首先运行;它将在消费者有机会运行之前全部执行。 (对于消费者来说,它需要获取生产者所持有的sb上的锁。)生产者调用notify,但没有线程在等待,所以它没有效果。然后消费者等待,并没有通知,所以它无限期地挂起。

如果消费者先运行,则代码将正常结束。

避免编写代码依赖于一个线程发生在另一个之前运行,因为你没有什么先执行控制。当您等待时,您需要在测试条件的循环内执行此操作。其中一个原因是,如果在线程开始等待之前设置了条件,则线程可以知道不会等待。

更改代码以使用条件:在Java中

import java.io.*; 

class producer implements Runnable { 
    StringBuffer sb; 
    boolean done = false; 
    producer() { 
     sb=new StringBuffer(""); 
    } 

    public void run() { 

     synchronized(sb) { 

      for(int i=0;i<10;i++) { 
       try { 
        sb.append(i+" "); 
        System.out.println("Appending ... "); 
       } catch (Exception e) {} 
      } 
      sb.notify(); 
      done = true; 
     } 


    } 
} 

class consumer implements Runnable { 
    producer p; 
    consumer(producer pp) { 
     this.p=pp; 
    } 

    public void run() { 
     System.out.println("Rreached"); 
     synchronized(p.sb) { 
      try { 
       while (!p.done) { 
       p.sb.wait(); 
       } 
      } catch (Exception e) {} 

      System.out.println(p.sb); 
     } 
    } 
} 


public class Communication { 
    public static void main (String [] args) throws Exception { 
     producer p= new producer(); 
     consumer c= new consumer(p); 

     Thread t1= new Thread(p); 
     Thread t2= new Thread(c); 
     t2.start(); 
     t1.start(); 
    } 
} 
1

所以我一直在一个简单的等待/通知例如出于某种原因,我一直没能得到它的正常运行。

您的代码存在的问题是notify()未被保留。如果producer输入​​块第一个,则consumer将不能输入它并转到wait,直到producer退出循环并结束。由于所有notify调用发生里面的​​块,到consumer到达wait()时,producer已完成并不再呼叫notify。这意味着consumer已挂起。

即使你开始consumer第一,你仍然有可能导致producer进入其​​块第一的竞争条件 - 因为consumer调用System.out.println()这需要时间,这是尤其如此。虽然不是“修复”,如果你的​​呼叫前减慢producerThread.sleep(100),你应该看到,现在的工作,因为consumer到达其wait()之前producer锁定它。

有几种方法可以让你正确地解决这个问题。通常我们使用wait/notify consumer应该检查的另一个变量。在你的情况下,这可能是sb变量本身,因为它受到保护。所以consumer可以这样做:

synchronized (p.sb) { 
    try { 
     // do we need to wait to see if the producer added stuff? 
     if (p.sb.length() == 0) { 
      p.sb.wait(); 
     } 
    } catch (InterruptedException e) { 
     // this is always a good pattern to preserve the interrupt flag 
     Thread.currentThread().interrupt(); 
     return; 
    } 
} 
相关问题