1

所以这是我第一次涉足线程,到目前为止它让我发疯。我的问题似乎是某种导致我的消费者线程挂起的同步错误。我查看了其他代码以及几乎所有我能找到的东西,但我找不到我的错误是什么。在 Eclipse 中执行的代码和通过命令行中的 javac 执行的代码之间似乎也存在差异。

意图 - 使用有界缓冲区(具有 1000 个插槽)创建和消耗 1,000,000 个双精度数。仅使用通知和等待。

问题 - 在 Eclipse 中,消费者线程偶尔会挂起大约 940,000 次迭代,但其他时候会完成。在命令行中,消费者线程总是挂起。

4

4 回答 4

2

Wait()方法可以以虚假方式中断(即不通知),请参阅此处。因此,您需要替换所有if (condition) { wait(); }to while (condition) { wait(); }。也许这就是原因。

于 2012-04-11T08:03:35.210 回答
1

你搞砸了addPlaceand getPlace。为了清楚起见,我将它们重命名nextWritenextRead. 所以这是你的 add()

if ((nextWrite + 1) == nextRead) {
  wait();
}
buff[nextWrite] = someRandomNumber;
nextWrite = (nextWrite + 1) % 1000;
notify();

这是你的 get()

if (nextRead == nextWrite) {
  wait();
}

逻辑错误很明显:since nextWriteis in the range [0; 999],nextWrite + 1将在 [1; 1000],但nextRead只能在[0;999]。每次 nextWriteis999nextReadis 0wait调用将永远不会被执行,并且 Producer可能会覆盖尚未读取的数据。

生产者可能会在某个时候停止覆盖,但实际上在一个虚拟的多核机器上,其中一个核心比另一个快一百万倍,生产者将完成其run()并将终止,因为它仅在nextWrite + 1 == nextRead.

在我们想象的机器上,Consumer 将立即挂起(因此等于nextRead,Producer 上次设置的值,因为它运行恰好一百万次迭代,并且您的缓冲区计数器定义为),因为在那一刻它会收到通知Producer,但由于它已终止,因此永远不会收到任何通知。0nextWrite0i % 1000wait()

这是您的代码的改进(和工作)版本

编辑 我只是忘记了(微不足道的)解决方案:

public synchronized void add(double randomNumber) throws InterruptedException {
    if((nextWrite + 1) % 1000 == nextRead)
      wait();
    buff[nextWrite] = randomNumber;
    nextWrite = (nextWrite+1)%1000;
    notify();
}
于 2012-04-11T11:26:28.580 回答
0

几点:

  • 在 Java 中,通常第一个类名 char 必须是大写的:所以调用你的classProducer和.ConsumerBuffer
  • 当你说你在 Eclipse / 命令行上尝试这个时,这意味着你在 Eclipse 中运行生产者,在命令行中运行消费者,反之亦然?如果这样它不能工作,buffer则不会在两个进程之间共享。
于 2012-04-11T08:03:46.563 回答
0

好吧,我想我在你的程序中看到了逻辑问题。在标准的 Produce-Consume 例程中,您可以随心所欲地生产,但只有在生产了某些东西时才能消费。

在你的实现中,你生产,等到它的消费,然后再生产。

我认为这不是你打算做的。

从 add 方法中删除 wait() 块,并从 get() 方法中删除 notify()。另外,我不认为线程 join() 在 main 中是必需的。

编辑:你的 get() 方法总是进入 wait()。即使 Producer 已经完成了它的工作。想想看。如果 Producer 已经完成了它的工作,你不会得到 notify()。

于 2012-04-11T08:02:26.127 回答