可以多次通知唤醒同一个线程吗?
想象一下,你有一个典型的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
。 如果将某些内容放入workQueue
并workQueue.notify()
。 Consumer2
现在被置于运行队列中,但是排在第一位的Consumer1
。 这是一种常见的实现方式。 所以Consumer1
进入了一个从Consumer2
被通知的workQueue
中删除该项目。 如果workQueue
为空, Consumer2
必须再次测试,否则remove()
将抛出,因为队列再次为空。 有关比赛的更多详情,请参见此处。
同样重要的是要意识到已经记录了虚假的唤醒,因此while
循环可以防止线程在没有wait()
调用的情况下被唤醒。
所有这些说,如果您可以通过使用其他答案中建议的BlockingQueue
来减少您的生产者/消费者代码,那么您应该这样做。 BlockingQueue
代码已经解决了所有这些问题。
是的,你所描述的可能会发生。
如javadoc中所述, notify
唤醒任意线程。 因此,如果你的线程已经完成并且在下一次notify
之前调用了wait
,那么它就是唤醒的任意候选者之一。
我对multithreading应用程序有丰富的经验,我发现我使用以下两种模式之一:
-
有多个睡眠线程需要在事件中唤醒,它们唤醒的顺序无关紧要。 在这种情况下,我使用
notifyAll
来唤醒它们。 -
有一个睡眠线程需要在事件中醒来。 在这种情况下,我使用
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);