java - 为什么,在调用对象的等待方法之前,线程应该拥有完全相同的对象的监视器?
问题描述
我正在学习并发任务之间的合作,我已经得到了这个问题和一个可能的答案。我想确保我理解正确。
因此,调用a.wait()
它首先需要在对象上进行同步,a
或者更准确地说,线程必须成为a
监视器的所有者。a.wait()
据我了解,应该在同步上下文中调用(与a.notify()
/一起)的唯一原因a.notifyAll()
是避免竞争条件/ The Lost Wake-Up Problem。但理论上,可以通过在其他对象上同步来避免竞争条件调用a.wait()
,如下所示:a.notify()/a.notifyAll()
Thread #1:
synchronized(b) {
…
a.wait(); // here a thread owns only one monitor: the monitor of the object stored in the variable b, where a != b
…
}
Thread #2:
synchronized(b) {
…
a.notify(); // the same here
…
}
预期的行为是:线程#1 获取 的监视器b
,进入临界区,调用a.wait()
,然后等待a
并释放 的监视器b
。之后线程#2 获取 的监视器b
,进入临界区,调用a.notify()
(线程#1 得到通知),退出临界区并释放 的监视器b
,然后线程#1b
再次获取监视器并继续运行。因此,一次只能运行一个同步的关键部分,b
并且不会出现竞争条件。您可能会问,“但是如何wait()
知道b
在这种情况下使用该对象来避免竞争条件,因此必须释放其监视器?”。好吧,那样它就不会知道了。但也许wait
方法可以将对象作为参数来知道要释放哪个对象的监视器,例如,a.wait(b)
等待a
并释放 的监视器b
。但是没有这样的选择:a.wait()
导致线程等待a
并释放完全相同的监视器a
。时期。在上面的代码中调用wait
和notify
方法会导致IllegalMonitorStateException。
根据来自 Wikipedia 文章的信息wait
,方法的功能notify/notifyAll
内置于每个对象的监视器中,并在每个对象级别集成到同步机制的功能中。但它仍然没有解释为什么它首先是这样做的。
我对这个问题的回答:在调用一个对象的等待方法之前,一个线程应该拥有完全相同的对象的监视器,因为为了避免竞争条件,这个对象不仅是一个可行的选择,而且是最好的选择。
让我们看看在调用对象的wait
,notify/notifyAll
方法(以及一些相关的逻辑)之前是否不希望在对象上进行同步。如果对象具有与等待逻辑无关的其他同步方法(和/或对象上同步了一些关键部分),则在对象上同步会阻塞其他线程,这可能是不希望的。在这种情况下,总是可以重构相应的类,以便wait
andnotify/notifyAll
方法对一个对象进行操作,而其他同步方法/块对另一个对象进行操作。例如,一种可能的解决方案可能是专门为等待逻辑(等待和同步)创建一个专用对象,如下例所示:
public class A {
private Object lock = new Object(); // an object to synchronize and wait on for some waiting logic
private boolean isReady = false;
public void mOne() throws InterruptedException {
synchronized(lock) { // it blocks the instance of the Object class stored in the variable named lock
while(!isReady) {
lock.wait();
}
}
}
public void mTwo() {
synchronized(lock) { // the same here
isReady = true;
lock.notify();
}
}
synchronized public void mThree() { // it blocks an instance of A
// here is some logic having nothing to do with the above waiting and
// mThree() can run concurrently with mOne() and mTwo()
}
}
因此,无需在某些自愿对象上进行同步,以避免有关调用wait
、notify/notifyAll
方法的某种竞争条件。如果允许,只会造成不必要的混乱。
这是对的吗?或者,也许我在这里遗漏了一些东西。我想确保我不会错过任何重要的事情。
解决方案
推荐阅读
- bash - Bash脚本 - 如何在变量中的字符串值周围加上双引号?
- python - Frequency count across whole pandas dataframe
- ubuntu - Neo4j config file changes not having an effect
- python - 如何将数学添加到函数中以写入文本文件
- php - Problem with seeding users and their profiles in Laravel
- apache-spark-sql - Check whether udf is registered in pyspark
- angular - 救援专家:无法解析组件错误的所有参数
- c - Linux 进程堆栈被局部变量溢出(堆栈保护)
- c++ - C++ 部分模板模板特化
- android - 在android中触摸时获取图形的x和y坐标