可以多次通知唤醒同一个线程吗? [英] Can notify wake up the same thread multiple times?

查看:179
本文介绍了可以多次通知唤醒同一个线程吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Imagine you have a typical producer-consumer pattern in Java. To be a bit more efficient you want to use notify() and not notifyAll() when a new element is added to the queue. If two producer threads invoke notify, is it guaranteed that two distinct waiting consumer threads will be awoken? Or can it be that two notify()s fired shortly after each other cause the same comsumer thread to be queued for wakeup twice? I can't find the section is the API describing how this exactly works. Does java have some atomic internal operation for waking up threads exactly once?

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

If only one comsumer is waiting then the second notify would be lost, that is no problem.

推荐答案

我的回答有一些特定于实现的信息。它基于我对Sun JVM和其他线程库行为的工作知识。

My answer has some implementation specific information. It is based on my working knowledge of Sun JVM and other thread library behavior.


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

If two producer threads invoke notify, is it guaranteed that two distinct waiting consumer threads will be awoken?

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

No it is not. There is no guarantee that there will be any consumers awoken. What is guaranteed is that if there are 2 threads that are waiting, then 2 different threads will be put into the run queue.


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

Or can it be that two notify()s fired shortly after each other cause the same comsumer thread to be queued for wakeup twice?

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

No. Two notify() calls will not cause the same consumer thread to be queued twice. It may however, cause one thread to be awoken and there may not be other threads waiting, so the second notify() call may do nothing. Of course the thread could have been awoken and then gone right back waiting again and so get the second notify() call that way, but I don't think think that is what you are asking.


java是否有一些原子内部操作来唤醒线程一次?

Does java have some atomic internal operation for waking up threads exactly once?

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

Yes. The Thread code has a number of synchronization points. Once a thread has been notified it is moved out of the wait queue. Future calls to notify() will look into the wait queue and not find the thread.

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

One more important point. With producer/consumer models, always make sure you are testing the condition in a while loop. The reason is that there is race conditions with consumers that are blocked on the lock but not waiting on the condition.

 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 被通知了。如果 workQueue 为空, Consumer2 必须再次测试,否则 remove()将抛出,因为队列再次为空。有关比赛的更多详情,请参阅此处。

Consumer1 could be waiting on workQueue. Consumer2 could be blocked at the synchronized but in the run-queue. If something is put into the workQueue and workQueue.notify() is called. Consumer2 is put into run-queue now but is behind Consumer1 who is there first. This is a common implementation. So Consumer1 goes in a removes the item from the workQueue that Consumer2 was notified about. Consumer2 has to test again if the workQueue is empty otherwise remove() will throw because the queue is empty again. See here for more details of the race.

同样重要的是要认识到已经记录了虚假的唤醒,所以循环可以防止线程在没有等待的情况下被唤醒() call。

It is also important to realize that spurious wakeups have been documented so the while loop protects against a thread being awoken without a wait() call.

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

All this said, if you can reduce your producer/consumer code by using a BlockingQueue as recommended in other answers then you should do so. The BlockingQueue code already has solved all of these issues.

这篇关于可以多次通知唤醒同一个线程吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆