可以多次通知唤醒同一个线程吗?

想象一下,你有一个典型的Java生产者 – 消费者模式。 为了更高效,您希望在将新元素添加到队列时使用notify()而不是notifyAll() 。 如果两个生产者线程调用notify,是否可以保证唤醒两个不同的等待消费者线程? 或者可能是两个notify()在彼此之后不久触发导致同一个comsumer线程排队等待两次唤醒? 我找不到该部分是描述这是如何工作的API。 java是否有一些primefaces内部操作来准确唤醒线程一次?

如果只有一个消费者正在等待,那么第二个通知将丢失,这没有问题。

我的答案有一些具体的实施信息。 它基于我对Sun JVM和其他线程库行为的工作知识。

如果两个生产者线程调用notify,是否可以保证唤醒两个不同的等待消费者线程?

不它不是。 无法保证会有任何消费者醒来。 可以保证的是,如果有2个线程在等待,那么2个不同的线程将被放入运行队列中。

或者可能是两个notify()在彼此之后不久触发导致同一个comsumer线程排队等待两次唤醒?

否。两次notify()调用不会导致相同的消费者线程排队两次。 但是,它可能导致一个线程被唤醒,并且可能没有其他线程在等待,因此第二个notify()调用可能什么都不做。 当然线程可能已被唤醒然后再次等待再次等待,因此以这种方式获得第二个notify()调用,但我不认为这就是你所要求的。

java是否有一些primefaces内部操作来准确唤醒线程一次?

是。 Thread代码有许多同步点。 一旦线程被通知,它就会被移出wait队列。 对notify()未来调用将调查wait队列而不是找到该线程。

重要的一点。 对于生产者/消费者模型,始终确保您在while循环中测试条件。 原因是有消费者的竞争条件被锁定但没有等待条件。

  synchronized (workQueue) { // you must do a while here while (workQueue.isEmpty()) { workQueue.wait(); } workQueue.remove(); } 

Consumer1可能正在等待workQueue 。 可以在synchronized但在运行队列中阻止Consumer2 。 如果将某些内容放入workQueueworkQueue.notify()Consumer2现在被置于运行队列中,但是在第一位的Consumer1 。 这是一种常见的实现方式。 所以Consumer1进入了一个从Consumer2被通知的workQueue中删除该项目。 如果workQueue为空, Consumer2必须再次测试,否则remove()将抛出,因为队列再次为空。 有关比赛的更多详情,请参见此处。

同样重要的是要意识到已经记录了虚假的唤醒,因此while循环可以防止线程在没有wait()调用的情况下被唤醒。

所有这些说,如果您可以通过使用其他答案中建议的BlockingQueue来减少您的生产者/消费者代码,那么您应该这样做。 BlockingQueue代码已经解决了所有这些问题。

是的,你所描述的可能会发生。

如javadoc中所述, notify唤醒任意线程。 因此,如果你的线程已经完成并且在下一次notify之前调用了wait ,那么它就是唤醒的任意候选者之一。

我对multithreading应用程序有丰富的经验,我发现我使用以下两种模式之一:

  1. 有多个睡眠线程需要在事件中唤醒,它们唤醒的顺序无关紧要。 在这种情况下,我使用notifyAll来唤醒它们。

  2. 有一个睡眠线程需要在事件中醒来。 在这种情况下,我使用notify来唤醒它。

如果我遇到过多个睡眠线程并且我只想唤醒其中一个的情况,我会使用不同的设计来实现这一点。 基本上,我自己构建了一些东西,以便运行时环境不会做出任意决定。 我总是想知道什么会唤醒。

我将设计分解为这两种情况之一,或者我使用java.util.concurrent包中的一些东西。 我从来没有虚假通知的问题,但我也非常小心我用于锁定的对象。 我倾向于创建vanilla Object实例,其唯一目的是成为锁定操作的目标,但偶尔我会使用一个对象,其类类型定义良好并且在我的控制之下。

从javadoc for notify() :

选择要通知哪个线程“是任意的,并且由实现决定”

它几乎肯定不会是一个“公平”(用于计算机科学和并行性的术语)算法唤醒线程。 完全有可能同一个线程快速连续两次唤醒。 另请注意,虚假通知也是可能的。

一般来说,我同意建议使用BlockingQueue实现而不是自己执行此操作的注释。

您可以使用ReentrantLock获取公平的到货订单政策。 接口ReadWriteLock ,您获得生产者 – 消费者行为。 ReentrantReadWriteLock类结合了这两种function。

要么

您应该使用ArrayBlockingQueue ,此模式已经实现。

 int capacity = 10; boolean fair = true; new ArrayBlockingQueue(capacity, fair);