2

以下のプログラムをコピーして、IDE で実行してみてください。これは単純な Produce Consumer の実装です。1 つの Producer スレッドと 1 つの Consumer スレッドを使用すると問題なく動作しますが、それぞれ 2 つ使用すると失敗します。このプログラムがハングする理由を教えてください。または、他に何か問題があるのでしょうか。

import java.util.LinkedList;
import java.util.Queue;

public class PCQueue {

 private volatile Queue<Product> productQueue = new LinkedList<Product>();

 public static void main(String[] args) {
  PCQueue pc = new PCQueue();

  Producer producer = new Producer(pc.productQueue);
  Consumer consumer = new Consumer(pc.productQueue);

  new Thread(producer, "Producer Thread 1").start();
  new Thread(consumer, "Consumer Thread 1").start();

  new Thread(producer, "Producer Thread 2").start();
  new Thread(consumer, "Consumer Thread 2").start();
 }

}

class Producer implements Runnable {

 private Queue<Product> queue = null;

 private static volatile int refSerialNumber = 0;

 public Producer(Queue<Product> queue) {
  this.queue = queue;
 }

 @Override
 public void run() {

  while (true) {
   synchronized (queue) {
    while (queue.peek() != null) {
     try {
      queue.wait();
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
    queue.add(new Product(++refSerialNumber));
    System.out.println("Produced by: "
      + Thread.currentThread().getName() + " Serial Number: "
      + refSerialNumber);

    queue.notify();
   }
  }

 }
}

class Consumer implements Runnable {

 private Queue<Product> queue = null;

 public Consumer(Queue<Product> queue) {
  this.queue = queue;
 }

 @Override
 public void run() {
  while (true) {
   synchronized (queue) {
    while (queue.peek() == null) {
     try {
      queue.wait();
     } catch (InterruptedException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }

    Product product = queue.remove();
    System.out.println("Consumed by: "
      + Thread.currentThread().getName() + " Serial Number: "
      + product.getSerialNumber());

    queue.notify();

   }
  }

 }

}

class Product {
 private int serialNumber;

 public Product(int serialNumber) {
  this.serialNumber = serialNumber;
 }

 public int getSerialNumber() {
  return serialNumber;
 }
}
4

2 に答える 2

4

問題は、キューで待機している単一のスレッドのみを起動する queue.notify() を使用していることです。Producer 1 が notify() を呼び出し、Producer 2 を起動したとします。Producer 2 は、キューに何かがあることを確認したため、何も生成せず、単純に wait() 呼び出しに戻ります。これで、プロデューサーとコンシューマーの両方が通知を待っており、誰も通知するために残されている人はいません。

コードの問題を解決するには、queue.notifyAll() を使用して、wait() でブロックされたすべてのスレッドをウェイクアップします。これにより、コンシューマーが実行できるようになります。

注意として、実装では、キューに最大で 1 つの項目が含まれるように制限されています。したがって、2 番目のプロデューサーとコンシューマーのセットからは何のメリットもありません。より良い全体的な実装については、BlockingQueueを見て、たとえばArrayBlockingQueueのように制限できる実装を使用することをお勧めします。同期して待機/通知を使用する代わりに、単純にBlockingQueue.offer()BlockingQueue.take( ) を使用します。

于 2010-06-18T07:52:37.960 に答える
1

queue.notify() の代わりに queue.notifyAll() を使用

于 2010-06-18T07:52:07.970 に答える