2016-07-21 19 views
3

私は、Javaでwait()notify()を使用してプロデューサとコンシューマコードを作成しています。 スレッド0が作成され、produce()で呼び出され、スレッド1が作成され、consume()で呼び出されます。Javaでwait()とnotify()を使用してブロックしました

public class Processor { 

    private volatile List<Integer> list = new ArrayList<>(); 
    private final int MAX_CAPACITY = 5; 
    Object lock = new Object(); 

    public void produce() throws InterruptedException { 

    while (true) { 

     while (list.size() == MAX_CAPACITY) { 
     System.out.println("List is full! Producer is Waiting...."); 
     synchronized (lock) { 
      lock.wait(); 
     } 
     } 

     synchronized (lock) { 
     int random = new Random().nextInt(100); 
     list.add(random); 
     System.out.println("Added to list:" + random); 
     lock.notify(); 
     } 
    } 
    } 

    public void consume() throws InterruptedException { 

    while (true) { 

     while (list.size() == 0) { 
     System.out.println("List is empty!! Consumer is Waiting..."); 
     synchronized (lock) { 
      lock.wait(); 
     } 
     } 

     synchronized (lock) { 
     int i = list.remove(0); 
     System.out.println("Removed from list:" + i); 
     lock.notify(); 
     } 
    } 
    } 
} 

問題は、実行時に、プログラムがproduce()後に停止していることである:

List is empty!! Consumer is Waiting... 
Added to list:22 
Added to list:45 
Added to list:72 
Added to list:91 
Added to list:51 
List is full! Producer is Waiting.... 

私はここでの問題だかを理解することはできませんよ。私は何とかしてwhileループの​​ブロックをproduce()consume()というコードにラップすると問題を解決していることが分かりました。

produce()

synchronized (lock) { 
       while (list.size() == MAX_CAPACITY) { 
        System.out.println("List is full! Producer is Waiting...."); 

        lock.wait(); 
       } 

consume

synchronized (lock) { 
       while (list.size() == 0) { 
        System.out.println("List is empty!! Consumer is Waiting..."); 

        lock.wait(); 
       } 
      } 

ここでの問題は何ですか?スレッドの飢えやデッドロックのケースですか?

編集:通話クラス:

public class App { 
    public static void main(String[] args) { 
     final Processor processor = new Processor(); 

     Runnable r1 = new Runnable() { 

      @Override 
      public void run() { 
       try { 
        processor.produce(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 

      } 
     }; 

     Runnable r2 = new Runnable() { 

      @Override 
      public void run() { 
       try { 
        processor.consume(); 
       } catch (InterruptedException e) { 

        e.printStackTrace(); 
       } 
      } 
     }; 

     Thread t1 = new Thread(r1); 
     Thread t2 = new Thread(r2); 

     t1.start(); 
     t2.start(); 


    } 
} 
+0

どのようにして生産方法と消費方法を呼びますか?あなたはそのコードを投稿していただけますか? – Divers

+0

@Divers投稿は、呼び出し元クラスで編集されました。 – Anurag

+0

これはデッドロックではありません。デッドロックには2つ以上のロックが必要です。 – EJP

答えて

6

あなたがlist.size()を実行すると、それはスレッドセーフではなく、あなたが別のスレッドで変更した値が表示されます何guarenteeはありません。 JITは、あなたがそのスレッドでそれを変更していないことを検出すると、値をインライン化することさえできます。それはwhile(true)ループ内にもあるように使用すると、値の変化を確実にループ外​​ブロックを配置することにより

は(表示されている。

+0

私も追加したい: "notifyを使って' notifyAll'を使うことを好む –

+0

@MarkRotteveel - 悪いアドバイス。NotifyAllは非効率的であるあなたがそれに依存する必要があるなら、あなたの同期では何かが最適ではない –

+0

@StephenCおそらく、例えば、複数のプロデューサーやコンシューマーがいる場合は、コンシューマーが目を覚ます可能性があり、プロデューサーがプロデューサーを目覚めさせる可能性があり、何かが起こるまでに時間がかかるかもしれません。 –

1

​​外部ループを使用してread barrierを作成する。従って、プロデューサ/コンシューマはlist最新表示されますあなたの場合はループ内であなたがlist.size()をチェックしているところ。あなたは​​ブロック内whileループを移動した後、それが動作する理由がある。

私はまた、あなたがプロデューサ/コンシューマに単一同期ブロックを使用することをお勧めします。

たとえば、list.size() == 0がコンシューマ向けにfalseになると、lockオブジェクトのロックが解除され、次のステートメントでデータを消費するためにロックを再度取得しようとします。これは不要で非効率的です。それは次のようにする必要があります:

public void consume() throws InterruptedException { 

    while (true) { 
    synchronized (lock) { 
     while (list.size() == 0) { 
     System.out.println("List is empty!! Consumer is Waiting..."); 

     lock.wait(); 
     } 

     int i = list.remove(0); 
     System.out.println("Removed from list:" + i); 
     lock.notify(); 
    } 
    } 
}