Java 监视器的等待集是不是优先于条目集?
Posted
技术标签:
【中文标题】Java 监视器的等待集是不是优先于条目集?【英文标题】:Does Java monitor's wait set has a priority over entry set?Java 监视器的等待集是否优先于条目集? 【发布时间】:2019-11-09 12:23:10 【问题描述】:我想问你一个与 Java 中的多线程有关的问题。
我有一个监视器,多个线程都渴望拥有它。
同样在临界区this.wait()
内部会根据某些条件调用。
AFAIK,监视器有 2 组线程:
入口集 - 刚到达的线程聚集并等待轮到它们拥有监视器 wait set -this.wait()
等待被唤醒的线程
但是当notify
/notifyAll
被调用时,它们是如何竞争的呢?
等待集中的线程在获取监视器方面是否优先于条目集中的线程,或者它们是否移动到条目集中?
我可以确定在notify
的情况下,下一个执行的线程将是等待集中的线程吗?
【问题讨论】:
Re,“我有一个监视器,多个线程都渴望拥有它。”这是一个糟糕的开始。在你的程序中拥有一个高度竞争的锁定几乎从来都不是一件好事。我会深入思考是否可以通过某种方式改变程序的架构,从而减少对锁定的需求。如果这不可能,那么我会深入思考是否可以更改架构以在单个线程中完成所有操作。 @SolomonSlow 只是添加了一些戏剧性 【参考方案1】:没有。调度程序负责哪个线程下一个获得锁。可能是等待集中的一个收到通知。它可能是一个刚刚到达并且尚未进入等待集的线程。假设刚刚得到通知的线程接下来会得到监视器是不安全的。
标准建议是在循环中调用 wait 来检查正在等待的条件:
synchronized (lock)
while (!condition)
lock.wait();
...
这样,当一个线程退出等待时,它会与任何尚未等待的线程进行相同的检查,以确定是否继续。
如果您需要公平,您希望等待时间最长的线程接下来获取锁,那么您可以尝试 java.util.concurrent.locks 中的显式锁之一,例如ReentrantLock,但请阅读罚款打印:
这个类的构造函数接受一个可选的公平参数。当设置为 true 时,在争用情况下,锁有利于授予对等待时间最长的线程的访问权限。否则,此锁不保证任何特定的访问顺序。使用由许多线程访问的公平锁的程序可能会显示出比使用默认设置的程序更低的整体吞吐量(即更慢;通常要慢得多),但在获取锁和保证不会出现饥饿的情况下具有较小的时间差异。但是请注意,锁的公平性并不能保证线程调度的公平性。因此,使用公平锁的许多线程之一可能会连续多次获得它,而其他活动线程没有进展并且当前没有持有锁。另请注意,不定时的 tryLock 方法不遵守公平设置。即使其他线程正在等待,如果锁可用,它也会成功。
【讨论】:
如果您需要一个更“公平”的流程,您可以使用并发工具包中的 Lock。 "假设刚刚得到通知的线程接下来会得到监视器是不安全的。" - 这不是真的。线程仅在拥有监视器的情况下调用wait()
,并且一旦收到通知,即wait()
返回,则保证它再次拥有监视器。如果不隐式和原子地重新获取监视器,则不会收到通知。
抱歉,我问了更多线程从等待集移动到入口集吗?
@Pavel:线程到达等待集的方式是当它们调用等待时。当他们从等待中醒来时,他们会移动到入口集,在那里他们必须重新获得锁才能继续前进。也无需道歉。
@JimmyB 看来你误解了这句话“假设刚刚收到通知的线程接下来会得到监视器是不安全的”。注意下一个这个词。它所说的只是你自己承认的:“当然,当一个线程等待重新获取时,任何数量的其他线程都可以获取并释放监视器。”没有人说wait()
可以在没有拥有显示器的情况下返回。但是由于任意线程可以获取和释放监视器,因此不能保证在调用notify()
之前生成的condition
在wait()
返回时仍然是true
。因此,循环。以上是关于Java 监视器的等待集是不是优先于条目集?的主要内容,如果未能解决你的问题,请参考以下文章
java.util.concurrent.locks.Condition文档说明
条目二十六《iterator优先于const_iteratorreverse_iterator以及const_reverse_iterator》