首页 > 解决方案 > 为什么,在调用对象的等待方法之前,线程应该拥有完全相同的对象的监视器?

问题描述

我正在学习并发任务之间的合作,我已经得到了这个问题和一个可能的答案。我想确保我理解正确。

因此,调用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。时期。在上面的代码中调用waitnotify方法会导致IllegalMonitorStateException

根据来自 Wikipedia 文章的信息wait,方法的功能notify/notifyAll内置于每个对象的监视器中,并在每个对象级别集成到同步机制的功能中。但它仍然没有解释为什么它首先是这样做的。

我对这个问题的回答:在调用一个对象的等待方法之前,一个线程应该拥有完全相同的对象的监视器,因为为了避免竞争条件,这个对象不仅是一个可行的选择,而且是最好的选择。

让我们看看在调用对象的wait,notify/notifyAll方法(以及一些相关的逻辑)之前是否不希望在对象上进行同步。如果对象具有与等待逻辑无关的其他同步方法(和/或对象上同步了一些关键部分),则在对象上同步会阻塞其他线程,这可能是不希望的。在这种情况下,总是可以重构相应的类,以便waitandnotify/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() 
    }
}

因此,无需在某些自愿对象上进行同步,以避免有关调用waitnotify/notifyAll方法的某种竞争条件。如果允许,只会造成不必要的混乱。

这是对的吗?或者,也许我在这里遗漏了一些东西。我想确保我不会错过任何重要的事情。

Oracle 文档:waitnotifynotifyAll

标签: javamultithreadingconcurrencywaitthread-synchronization

解决方案


推荐阅读