0

我正在尝试Java线程生产者和消费者程序。 但消费者线程始终处于等待状态。Java线程生产者和消费者程序问题

我无法调试的问题,为什么消费者线程总是转到待机状态或生产者未通知消费者线程

请帮我解决这个问题。程序如下。

通信器类调用这两个生产国和消费类

public class Communicator { 

    Thread t = null; 
    Thread t1 = null; 

    public void runThread() { 
     Producer p = new Producer(); 
     Consumer c = new Consumer(p); 
     t = new Thread(p); 
     t1 = new Thread(c); 
     t.start(); 
     t1.start(); 
     Thread tr = new Thread() { 
      public void run() { 
       for (int i = 0; i < 30; i++) { 
        System.out.println("t::::::::::::: " + t.getState()); 
        System.out.println("t1::::::::::::: " + t1.getState()); 
        try { 
         Thread.sleep(2000); 
        } catch (InterruptedException ie) { 
         ie.printStackTrace(); 
        } 
       } 
      } 
     }; 
     tr.start(); 
    } 

    public static void main(String[] args) { 
     Communicator c = new Communicator(); 
     c.runThread(); 
    } 
} 

这是生产类数据中追加的StringBuffer,并通知消费者类

public class Producer extends Thread { 
     public StringBuffer sb; 

     public Producer() { 
      sb = new StringBuffer(); 
     } 

     public void run() { 
      synchronized (sb) { 
       try { 
        System.out.println("Bala"); 
        sb.append("murugan"); 
        sb.notify(); 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     } 

    } 

下面是消费类代码。它等待从生产者类获得通知。

public class Consumer extends Thread { 
    public Producer p; 

    public Consumer(Producer p) { 
     this.p = p; 

    } 

    public void run(){ 
     synchronized (p.sb) { 
      try { 

       p.sb.wait(); 

      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      System.out.println(p.sb); 
     } 
    } 


} 
+1

在'Consumer'类中的'wait()'之前,你应该做你的业务逻辑来处理字符串缓冲区中的东西。只有在没有可用的数据可供处理时,才应该使用wait()。 –

+0

此问题可能会有用:http://stackoverflow.com/questions/37683895/wait-and-notify-in-consumer-and-producer-threads/37686902#37686902。它删除等待并通过BlokcingQueues通知。用字符串替换Interger –

+0

@ user3509105请参考下面的答案。希望这会有所帮助。方法'notify()'只唤醒那些当前没有醒来但正在等待通知的符合条件的线程,以便他们能够获得锁。希望它会有所帮助。 –

回答

0

Producer已经结束,它已经调用notify()Consumer之前调用wait()

由于ProducerConsumerextendsThread,更新Communicator类这样的:

public class Communicator { 
    public void runThread() { 
     final Producer p = new Producer(); 
     final Consumer c = new Consumer(p); 

     p.start(); 
     c.start(); 

     Thread tr = new Thread() { 
      public void run() { 
       for (int i = 0; i < 30; i++) { 
        System.out.println("t::::::::::::: " + p.getState()); 
        System.out.println("t1::::::::::::: " + c.getState()); 
        try { 
         Thread.sleep(2000); 
        } catch (InterruptedException ie) { 
         ie.printStackTrace(); 
        } 
       } 
      } 
     }; 
     tr.start(); 
    } 

    public static void main(String[] args) { 
     Communicator c = new Communicator(); 
     c.runThread(); 
    } 
} 

如果Producer尚未终止[if (p.getState() != Thread.State.TERMINATED)],这是唯一的一次Consumer等待:

public class Consumer extends Thread { 
    public Producer p; 

    public Consumer(Producer p) { 
     this.p = p; 

    } 

    public void run() { 
     synchronized (p.sb) { 
      try { 

       if (p.getState() != Thread.State.TERMINATED) { 
        p.sb.wait(); 
       } 

      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      System.out.println(p.sb); 
     } 

    } 
} 
0

您的当前代码中几乎没有问题,其中消费者线程始终处于等待状态,而产品ucer已经终止。

此外,您StringBuffer对象必须是volatile使生产者线程写入将被刷新&提供给其他线程。

伴随于此,我已修改了ProducerConsumer代码,使其更逼真(在while循环一生产一些数据和其它接收的数据两者运行),如下所示:(I还添加1秒睡眠运行东西速度较慢,这样就可以了解事情做得更好):

Consumer类:

public class Producer extends Thread { 
     public volatile StringBuffer sb; 

     public Producer() { 
      sb = new StringBuffer(); 
      sb.append(""); 
     } 

     public void run() { 
      synchronized (sb) { 
       try { 
        while(true) { 
         Thread.sleep(1000); 
         if(sb.toString().equals("")) { 
          sb.append("murugan"); 
          System.out.println(" producing sb completed *** "); 
          sb.notify(); 
         } else { 
          sb.wait(); 
         } 
        } 
       } catch (Exception e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

Consumer类:

public class Consumer extends Thread { 
     public Producer p; 

     public Consumer(Producer p) { 
      this.p = p; 

     } 

     public void run(){ 
      synchronized (p.sb) { 
       try { 
        while(true) { 
         Thread.sleep(1000); 
         if(p.sb.toString().equals("")) { 
          p.sb.wait(); 
         } else { 
          String str = p.sb.toString(); 
          System.out.println(" consuming sb completed **** "+str); 
          p.sb.replace(0, str.length(), ""); 
          p.sb.notify(); 
         } 
        } 
       } catch (InterruptedException e) { 
        // TODO Auto-generated catch block 
        e.printStackTrace(); 
       } 
       System.out.println(p.sb); 
      } 
     } 
    } 
0

你的问题,“我无法调试的问题,为什么消费者线程总是进入等待状态,或生产者未通知消费者线程”。 其实你的消费者并不总是处于等待状态。 你可以把Thread.sleep(1000);在p.sb.wait()之前;在你的Consumer类中,你可以看到“consumerThread ::::::::::::: RUNNABLE”一次。IMG,您的客户代码运行得太快而无法获得等待状态,因此您错过了可运行状态。你可以从其他答案中了解更多。

0

这不是一个答案,而是一个建议......您可以使用BlockingQueue简化整个逻辑,将数据从生产者传输到消费者。所有的等待和通知将消失!

Producer(s) send data to be consumed calling BlockingQueue.offer(String) 

Consumer(s) wait (blocked) for data calling BlockingQueue.pool(); 
0

根据你的代码,Consumer Thread等待Producernotify约在StringBuffer附加的字符串。

  1. 如果Producer线程获得收购上shared StringBuffer object锁的机会(它进入synchronized block),然后Consumer Thread会在Blocked state将无法​​进入synchronized block)作为其也为竞争对手锁(两者竞争获取同一共享对象上的锁)。
  2. 生产者线程完成其执行,离开synchronized block并终止。 注意,通知代码将不会有任何影响,因为消费者线程尚未共享对象上等待,因为它是尚未进入synchronized块
  3. Consumer thread得到获取lock的机会,进入synchronized blockwaits为有人给共享对象发出通知。但由于生产者已经终止,没有人将通知发送给Consumer thread,并且它仍然处于Waiting状态。

修复:你的情况,你可以简单地确保Consumer thread首先启动并获得生产线前的锁。为此,您可以让主线程在启动消费者线程后休眠一段时间。

t = new Thread(p); 
t1 = new Thread(c); 
t1.start(); 
try { 
     Thread.sleep(1000); 
    }catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 
t.start(); 

要点:如果你只有2个线程,一个线程应调用notifywait。其他线程收到通知后,只有锁定竞争线程才能获得锁定并完成其工作。完成工作后,它应该调用通知,并等待另一个线程完成工作并通知完成。通过这种方式,两个线程将有机会一个接一个地完成他们的工作。