7.4 为什么wait()必须始终处于synchronized锁住的代码块中? | Java Debug 笔记

215 阅读2分钟

本文正在参加「Java主题月 - Java Debug笔记活动」,详情查看<活动链接>

提问:为什么wait()必须始终处于synchronized锁住的代码块中?

我们都知道,为了调用Object.wait(),wait()必须始终处于synchronized锁住的代码块中。否则将引发IllegalMonitorStateException。但是,设置这种限制的原因是什么?

我知道wait()会释放监视器,但是为什么我们需要通过使特定的synchronized块来显式获取监视器,然后通过调用wait()来释放监视器?

如果可以wait()在synchronized块之外调用并保留其语义-挂起调用者线程,可能造成什么损害?

回答1:

wait()仅在还有notify()时才有意义,因此它始终与线程之间的通信有关,并且需要同步才能正常工作。 有人可能会争辩说这应该是隐式的,但这并没有真正的帮助,原因如下:

从语义上讲,您永远不会只是在wait的状态中。 您需要满足一些条件,如果不是,请等到满足。 所以你真正要做的是

if(!condition){
    wait();
}

但是条件是由单独的线程设置的,因此为了正确执行此工作,您需要synchronized。

还有其他一些问题,仅仅是因为线程退出等待并不意味着您要寻找的条件是正确的:

您可能会得到虚假的唤醒(这意味着线程可以从等待中唤醒,而从未收到通知),或者可以设置条件,但是第三个线程会在等待线程唤醒(并重新获取监视器)时再次使条件变为假。

为了处理这些情况,您真正需要的始终是这种变化:

synchronized(lock){
    while(!condition){
        lock.wait();
    }
}

最好呢,根本不要用 synchronize这个关键字,而要使用java.util.concurrent包中提供的抽象。