Java 通知可以多次唤醒同一线程吗?
假设您在Java中有一个典型的生产者-消费者模式。为了提高效率,在队列中添加新元素时,您希望使用Java 通知可以多次唤醒同一线程吗?,java,multithreading,synchronization,notify,Java,Multithreading,Synchronization,Notify,假设您在Java中有一个典型的生产者-消费者模式。为了提高效率,在队列中添加新元素时,您希望使用notify()而不是notifyAll()。如果两个生产者线程调用notify,是否保证两个不同的等待消费者线程将被唤醒?或者可能是两个notify()s在彼此之后不久触发,导致同一个消费者线程排队等待唤醒两次?我找不到API中描述其工作原理的部分。java是否有一些原子内部操作来唤醒线程一次 如果只有一个消费者在等待,那么第二个通知就会丢失,这没有问题 来自: 通知哪个线程的选择“是任意的,由实现
notify()
而不是notifyAll()
。如果两个生产者线程调用notify,是否保证两个不同的等待消费者线程将被唤醒?或者可能是两个notify()
s在彼此之后不久触发,导致同一个消费者线程排队等待唤醒两次?我找不到API中描述其工作原理的部分。java是否有一些原子内部操作来唤醒线程一次
如果只有一个消费者在等待,那么第二个通知就会丢失,这没有问题 来自:
通知哪个线程的选择“是任意的,由实现自行决定”
对于唤醒线程来说,它几乎肯定不是一种“公平”(计算机科学和并行性中使用的术语)算法。完全有可能同一根线连续两次被唤醒。还请注意,虚假通知也是可能的
总的来说,我同意那些建议使用实现而不是自己这样做的评论。是的,您所描述的可能会发生。 如中所述,
notify
唤醒任意线程。因此,如果您的线程已完成,并且在下一个notify
之前调用了wait
,那么它将是唤醒的任意候选线程之一
我对多线程应用程序有丰富的经验,我发现我使用以下两种模式之一:
notifyAll
唤醒它们notify
唤醒它对象
实例,其唯一目的是作为锁定操作的目标,但偶尔我会使用一个类类型定义良好且在我控制下的对象。您可以使用它来获得公平到达订单策略。在界面中,您可以获得生产者-消费者行为。该类结合了这两种功能
或
您应该使用,其中该模式已经实现
int capacity = 10;
boolean fair = true;
new ArrayBlockingQueue(capacity, fair);
我的回答包含一些特定于实现的信息。它基于我对Sun JVM和其他线程库行为的工作知识 如果两个生产者线程调用notify,是否保证两个不同的等待消费者线程将被唤醒 不,不是。不能保证会有消费者被唤醒。可以保证的是,如果有两个线程正在等待,那么将有两个不同的线程放入运行队列 或者可能是两个
notify()
s在彼此之后不久触发,导致同一个消费者线程排队等待唤醒两次
否。两次notify()
调用不会导致同一使用者线程排队两次。但是,它可能会导致一个线程被唤醒,并且可能没有其他线程在等待,因此第二个notify()
调用可能不会执行任何操作。当然,线程可能已经被唤醒,然后又重新返回等待,这样就可以获得第二个notify()
调用,但我认为这不是您所要求的
java是否有一些原子内部操作来唤醒线程一次
对。线程
代码有许多同步点。一旦一个线程收到通知,它就会移出wait
队列。以后对notify()
的调用将查看wait
队列,而不会找到线程
还有一点很重要。对于生产者/消费者模型,始终确保在while
循环中测试条件。原因是存在与消费者的竞争条件,消费者在锁上被阻止,但没有等待条件
synchronized (workQueue) {
// you must do a while here
while (workQueue.isEmpty()) {
workQueue.wait();
}
workQueue.remove();
}
Consumer1
可能正在等待workQueue
Consumer2
可能在synchronized
处被阻止,但在运行队列中。如果将某个内容放入工作队列
并调用workQueue.notify()
Consumer2
现在被放入运行队列,但在Consumer1
后面,谁先到。这是一个常见的实现。因此,Consumer1
将从workQueue
中删除通知Consumer2
的项目Consumer2
必须再次测试workQueue
是否为空,否则将抛出remove()
,因为队列再次为空。请看这里
同样重要的是要认识到,已经记录了虚假唤醒,因此while
循环可以防止线程在没有wait()
调用的情况下被唤醒
综上所述,如果您可以按照其他答案中的建议,通过使用
阻止队列来减少生产者/消费者代码,那么您应该这样做。BlockingQueue
代码已经解决了所有这些问题。为什么不使用BlockingQueue实现呢?我同意Mauricio的观点。尽可能避开等待()/notify()——这是一个充满竞争条件的大黄蜂窝