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。其他線程收到通知後,只有鎖定競爭線程才能獲得鎖定並完成其工作。完成工作後,它應該調用通知,並等待另一個線程完成工作並通知完成。通過這種方式,兩個線程將有機會一個接一個地完成他們的工作。