0

我正在使用多线程解决经典的生产者-消费者问题。我在代码中使用wait()notifyAll()。我的问题是当notifyAll通知其他等待线程恢复时,它不会立即恢复。这是为什么?代码如下

public class ConsumerProducer {

 private  int count;

 public synchronized void consume() {
  while (count == 0) {  // keep waiting if nothing is produced to consume
   try {
    wait(); // give up lock and wait
   } catch (InterruptedException e) {
    // keep trying
   }
  }

  count--;              // consume
  System.out.println(Thread.currentThread().getName() + " after consuming " + count);
 }

 public synchronized void produce() {
  count++;             //produce
  System.out.println(Thread.currentThread().getName() + " after producing " + count);
  notifyAll();         // notify waiting threads to resume
 }

}

客户端代码:

public class ConsumerProducerTest implements Runnable {

 boolean isConsumer;
 ConsumerProducer cp;

 public ConsumerProducerTest(boolean isConsumer, ConsumerProducer cp) {
  this.isConsumer = isConsumer;
  this.cp = cp;
 }

 public static void main(String[] args) {
  ConsumerProducer cp = new ConsumerProducer(); //shared by both threads to communicate

  Thread producer = new Thread(new ConsumerProducerTest(false, cp));
  Thread consumer = new Thread(new ConsumerProducerTest(true, cp));

  producer.start();
  consumer.start();
  //producer.start();


 }

 @Override
 public void run() {
  for (int i = 1; i <= 10; i++) {
   if (!isConsumer) {
    cp.produce();
   } else {
    cp.consume();
   }
  }

输出是:

Thread-1 after producing 1
Thread-1 after producing 2
Thread-1 after producing 3
Thread-1 after producing 4
Thread-1 after producing 5
Thread-1 after producing 6
Thread-1 after producing 7
Thread-1 after producing 8
Thread-1 after producing 9
Thread-1 after producing 10
Thread-2 after consuming 9
Thread-2 after consuming 8
Thread-2 after consuming 7
Thread-2 after consuming 6
Thread-2 after consuming 5
Thread-2 after consuming 4
Thread-2 after consuming 3
Thread-2 after consuming 2
Thread-2 after consuming 1
Thread-2 after consuming 0

打印完上面的第一行之后,调用 notifyAll 并且等待线程应该恢复,打印Thread-2 after consuming 0。我看到的问题Thread-1 (Thread-1 after producing)是完成后,恢复 Thread-2。但两者都应该同时发生吗?请帮帮我。谢谢 编辑:在 main 方法中有 join() ,并没有改变输出:

 producer.start();
  producer.join();
  consumer.start(); 
4

3 回答 3

8

但两者都应该同时发生吗?

这里的问题是生产(和消费)只需要很少的时间。您正在看到所谓的(非关键)竞争条件,生产者甚至能够在消费者开始之前生产所有 10 个项目。竞争条件是指您的两个线程相互竞争以生产和消费,在这种情况下不是错误。

如果您将测试增加到(比方说)100000 个项目,您会看到生产和消费消息混合在一起,尽管即使那样您可能会看到长度为 10 或更多的消息块producingconsuming消息。

另一件要尝试的事情是首先启动消费者并Thread.sleep(10);在启动它之后放置一个,以便它等待生产者。然后,消费者将有时间打电话,并在第一个被调用时立即wait()从 转移WAIT到。但即便如此,竞争条件也可能会显示. 这就是多线程应用程序的异步特性。BLOCKEDnotifyAll()producingconsuming

ps 正确处理总是一个很好的模式InterruptedException。您应该执行以下操作:

try {
    wait(); // give up lock and wait
} catch (InterruptedException e) {
    // reset the thread interrupt flag
    Thread.currentThread().interrupt();
    // probably stopping the thread is best
    return;
}
于 2013-08-29T22:04:36.117 回答
3

正如这里的大多数人告诉你的那样,程序输出并没有错。理解为什么是你的挑战。

调用 .notifyAll() 并不能保证通知线程(生产者)会立即停止执行,也不能保证等待通知的线程(消费者)会立即开始执行。这两个线程可以“同时”执行,也可以不执行。线程受操作系统线程调度程序的支配。如果您要增加 for 循环中的迭代次数,您可能会看到消息开始交错,因为操作系统在 CPU 上和关闭两个线程之间交换,但它们不一定会交错 - 没有说它们必须 -他们可能会。也不能保证交织的粒度(如果它发生的话)。

发送通知只是保证如果另一个线程在“等待”中被阻塞,该线程最终将被唤醒。此外,消费者不一定会总共醒来 10 次。通知仅对在“等待”调用中被阻塞的线程有意义。您可能会发送 10 个通知,但只进入和退出对 .wait 的调用一次(或从不)。如果您在 .wait 中没有线程时发送通知,则该通知将“丢失”。这就是为什么其他一些信息通常与等待/通知结合使用的原因,例如计数、布尔值或队列。

于 2013-08-29T22:33:16.057 回答
2

从 notify(和 notifyAll)文档

在当前线程放弃对该对象的锁定之前,被唤醒的线程将无法继续。被唤醒的线程将以通常的方式与可能正在积极竞争以在该对象上同步的任何其他线程竞争;例如,被唤醒的线程在成为下一个锁定该对象的线程时不享有可靠的特权或劣势。

您所看到的只是这两个线程的调度方式。

于 2013-08-29T22:08:21.637 回答