Java 生产者和消费者线程挂起

Java 生产者和消费者线程挂起,java,multithreading,command-line,Java,Multithreading,Command Line,这是我第一次尝试线程,到目前为止,它让我发疯。我的问题似乎是某种同步错误导致消费者线程挂起。我看过其他代码,几乎所有我能找到的东西,但我找不到我的错误是什么。在Eclipse中执行的代码和通过命令行中的javac执行的代码之间似乎也存在差异 意图-使用有界缓冲区(有1000个插槽)创建并消耗1000000个双倍。仅使用通知和等待 问题-在Eclipse中,使用者线程偶尔会挂起大约940000次迭代,但其他时间会完成。在命令行中,使用者线程总是挂起。嗯,我想我看到了程序中的逻辑问题。 在stand

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

意图-使用有界缓冲区(有1000个插槽)创建并消耗1000000个双倍。仅使用通知和等待


问题-在Eclipse中,使用者线程偶尔会挂起大约940000次迭代,但其他时间会完成。在命令行中,使用者线程总是挂起。

嗯,我想我看到了程序中的逻辑问题。 在standart生产-消费程序中,您可以生产任意数量的产品,但您只能在有产品生产的情况下进行消费

在您的实现中,您生产,等待它被消耗,然后再次生产

我想这不是你想做的

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

编辑:并且get()方法总是进入wait()。即使制片人已经完成了工作。想想看。如果制作人已经完成了它的工作,您将不会得到notify()

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

有几点:

  • 在Java中,通常第一个类名char必须是大写的:因此调用类
    Producer
    Consumer
    Buffer
  • 当您说您在Eclipse/命令行上尝试此操作时,这意味着您在Eclipse中运行producer,在命令行中运行Consumer,反之亦然?如果无法工作,则两个进程之间不共享
    缓冲区

你搞砸了
addPlace
getPlace
。为了清楚起见,我将它们重命名为
nextWrite
nextRead
。这就是你的add()

这是你的get()

逻辑错误显而易见:由于
nextWrite
在[0;999]范围内,
nextWrite+1
将在[1;1000]范围内,但
nextWrite
只能在[0;999]范围内。每次
nextWrite
999
nextrade
0
,将永远不会执行
wait
调用,并且生产者可能会覆盖尚未读取的数据

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

在我们想象的机器上,消费者将在
nextRead
变为
0
时挂起(因此等于
nextWrite
,生产者最后将其设置为
0
,因为它运行了100万次迭代,而缓冲区计数器被定义为
i%1000
),因为此时它将
等待()
,但由于制作人的通知已终止,因此不会收到任何通知

(和工作)代码的版本

编辑 我只是忘记了(琐碎的)解决方案:

public synchronized void add(double randomNumber) throws InterruptedException {
    if((nextWrite + 1) % 1000 == nextRead)
      wait();
    buff[nextWrite] = randomNumber;
    nextWrite = (nextWrite+1)%1000;
    notify();
}

最好停止使用“支持”虚假唤醒的操作系统:)哪个操作系统支持?我不知道这一点,所以很有趣:)好吧,只有U**x类操作系统支持这一功能。这是企鹅特有的,我看你已经试过了“//if(I>999000)”。这是否表示使用者线程尚未完成,因为一个或多个队列对象已“丢失”,或者使用者因其他原因卡住?您可能希望尝试将整数排队,并在使用者中检查接收到的每个值(第一个除外)是否比最后一个值多一个,如果不是,则立即停止/例外。随机双倍几乎是你可以选择排队的最糟糕的事情,因为在消费者中很难检查/比较它们。+1对我来说很好。我很惊讶,我没有注意到在检查缓冲区是否已满时未能包装索引。遗憾的是,OP并没有简单地将递增的整数后置,并在消费者中检查它们——一个问题的关闭是显而易见的。当然,打印整数他很快就会发现这一点。也许他不会在这里发布:P我也没有注意到,直到我用有意义的名字重构了他的代码,逻辑错误立即出现。非常感谢你的帮助,但是我认为这已经扭转了问题。它现在在生产者线程中的999999处间歇性挂起。使用者线程终止,如果生产者位于9999999,则不可能终止。@user972425
add
get
中的
if
前置码应为
while(){wait();}
if (nextRead == nextWrite) {
  wait();
}
public synchronized void add(double randomNumber) throws InterruptedException {
    if((nextWrite + 1) % 1000 == nextRead)
      wait();
    buff[nextWrite] = randomNumber;
    nextWrite = (nextWrite+1)%1000;
    notify();
}