我的回答有一些特定于实现的信息。它基于我对 Sun JVM 和其他线程库行为的工作知识。
如果两个生产者线程调用通知,是否保证两个不同的等待消费者线程将被唤醒?
不,不是。不能保证会有任何消费者被唤醒。可以保证的是,如果有 2 个线程在等待,那么 2 个 不同的 线程将被放入运行队列。
或者是两个notify()s 在彼此之后不久触发导致同一个消费者线程排队等待唤醒两次?
没有。两次notify() 调用不会导致同一个消费者线程排队两次。然而,它可能会导致一个线程被唤醒并且可能没有其他线程在等待,因此第二个notify() 调用可能什么都不做。当然,线程可能已经被唤醒,然后又回去等待,然后以这种方式获得第二个 notify() 调用,但我认为这不是你要问的。
java 是否有一些原子内部操作来唤醒线程一次?
是的。 Thread 代码有多个同步点。一旦一个线程得到通知,它就会被移出wait 队列。未来对notify() 的调用将查看wait 队列并且找不到线程。
还有一点重要。对于生产者/消费者模型,请始终确保您在 while 循环中测试条件。原因是存在竞争条件,消费者在锁上被阻塞但不等待条件。
synchronized (workQueue) {
// you must do a while here
while (workQueue.isEmpty()) {
workQueue.wait();
}
workQueue.remove();
}
Consumer1 可能正在等待workQueue。 Consumer2 可以在 synchronized 处被阻止,但在运行队列中。如果将某些内容放入workQueue 并调用workQueue.notify()。 Consumer2 现在被放入运行队列,但在 后面 Consumer1 谁是第一个出现的。这是一个常见的实现。所以Consumer1 会从workQueue 中删除Consumer2 收到通知的项目。 Consumer2 必须再次测试 workQueue 是否为空,否则 remove() 将抛出,因为队列再次为空。在此处查看more details of the race。
同样重要的是要意识到虚假唤醒已被记录,因此while 循环可防止线程在没有wait() 调用的情况下被唤醒。
所有这一切,如果您可以按照其他答案中的建议使用BlockingQueue 来减少您的生产者/消费者代码,那么您应该这样做。 BlockingQueue 代码已经解决了所有这些问题。