2014-12-19 58 views
2

这是在其中,我希望得到如下输出一个消费者 - 生产者问题:
认沽:0
得到:0
认沽:1
得到:1
.. ..等等。
但与此形成对比的是,尽管使用了wait()和notify()方法,但Consumer类仍然多次使用相同的q值,并且生产者类超出了使用者的范围。我怎样才能获得同步输出?
同步实施:Java的

这是QFixed类:(定义把()和get()方法)

class QFixed{ 
    int n; 
    boolean valueset = false; 

    synchronized int get(){ 
     if(!valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     System.out.println("Got: " +n); 
     valueset = false; 
     notify(); 
     return n; 
    } 

    synchronized void put(int n){ 
     if (valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     this.n = n; 
     valueset = true; 
     System.out.println("Put: "+n); 
     notify(); 
    } 
} 

这ProducerFixed类:

class ProducerFixed implements Runnable{ 
    Q q; 
    Thread t; 
    public volatile boolean flag = true; 
     ProducerFixed(Q q){ 
      this.q = q; 
      t = new Thread(this,"Producer"); 
      t.start(); 
     } 

    @Override 
     public void run(){ 
      int i =0 ; 
      while(flag){ 
       q.put(i++); 
      } 
     } 

    void stop() { 
     flag = false; 
    } 
} 

这ConsumerFixed类:

class ConsumerFixed implements Runnable{ 
    Q q; 
    Thread t; 
    public volatile boolean flag = true; 

     ConsumerFixed(Q q){ 
      this.q = q; 
      t = new Thread(this,"Consumer"); 
      t.start(); 
     } 

    @Override 
     public void run(){ 
      while(flag){ 
       q.get(); 
      } 
     } 

    public void stop() { 
     flag = false; 
    } 
} 

这Producer_Consumer_Fixed类:

public class Producer_Consumer_Fixed { 
    public static void main(String arg[]){ 
     Q q = new Q(); 
     Producer p = new Producer(q); 
     Consumer c = new Consumer(q); 

     try { 
      Thread.sleep(100); 
     } catch (InterruptedException e) { 
      System.out.println("interrupted"); 
     } 

     p.stop(); 
     c.stop(); 
     try{ 
      p.t.join(); 
      c.t.join(); 
     }catch(InterruptedException e){ 
      System.out.println("interrupted"); 
     } 
    } 
} 
+2

请修正您的代码中的编译错误,您的代码似乎很好.. – TheLostMind 2014-12-19 11:16:11

+0

如何解决编译错误? – 2014-12-19 11:28:18

+1

您向我们展示的代码不能是您正在执行的代码。 '新Q()'< - 您提供的示例中没有名为'Q'的类。 – 2014-12-19 11:32:25

回答

4

if (flag) wait你QFixed get和put方法中使用的成语坏了,你应该使用while循环来代替。请参阅the Oracle tutorial on guarded blocks

有一次,我改变了类的名称以去除“固定”,并在Q类while更换if,像这样:

class Q { 
    int n; 
    boolean valueset = false; 

    synchronized int get(){ 
     while(!valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     System.out.println("Got: " +n); 
     valueset = false; 
     notify(); 
     return n; 
    } 

    synchronized void put(int n){ 
     while (valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     this.n = n; 
     valueset = true; 
     System.out.println("Put: "+n); 
     notify(); 
    } 
} 

我得到了输出开始

Put: 0 
Got: 0 
Put: 1 
Got: 1 
Put: 2 
Got: 2 
Put: 3 
Got: 3 
Put: 4 
Got: 4 
Put: 5 
Got: 5 
Put: 6 
Got: 6 
Put: 7 
Got: 7 
Put: 8 
Got: 8 
Put: 9 
Got: 9 
Put: 10 
Got: 10 
... 

每个值都被放置并获得一次,这是您想要的输出。

使用while循环有好几个原因是件好事。

等待线程放弃监视器,一旦唤醒它必须重新获取监视器,才能继续退出等待方法。这意味着其他线程可以使用监视器并可能更改同步所保护的数据的状态。一旦线程重新获得了监视器,它就需要再次检查该条件,然后才能知道它是否认为它实际发生了通知。否则,线程将根据陈旧的信息决定要执行什么操作。

在涉及三个或更多竞争线程的例子中,这将是一个大问题。但是,对于这个特定情况,我没有看到有问题的操作顺序。

while循环的另一个原因是,仅仅因为线程退出等待并不一定意味着发生了通知。根据javadoc for Object#wait

线程也可以唤醒而不会被通知,中断或超时,所谓的虚假唤醒。虽然这在实践中很少会发生,但应用程序必须通过测试应该引起线程被唤醒的条件来防范它,并且在条件不满足时继续等待。换句话说,等待应总是发生在循环中,像这样的:

synchronized (obj) { 
    while (<condition does not hold>) 
     obj.wait(timeout); 
    ... // Perform action appropriate to condition 
} 

这来自于JVM实现竞争条件;就像文件说的那样,它应该是一种罕见的现象。但这可能是问题的根源,在没有得到通知的情况下获得等待回报可能会产生像您所看到的那样的多重获益。