我们都知道为了调用Object.wait(),这个调用必须放在同步块中,否则抛出IllegalMonitorStateException。但是为什么要做出这样的限制呢?我知道wait()释放监视器,但为什么我们需要通过使特定块同步显式获取监视器,然后通过调用wait()释放监视器?

如果可以在同步块之外调用wait(),保留它的语义——挂起调用者线程,那么潜在的损害是什么?


当前回答

只有当同时存在notify()时,wait()才有意义,因此它总是关于线程之间的通信,并且需要同步才能正确工作。有人可能会说,这应该是隐含的,但这并没有真正的帮助,原因如下:

从语义上讲,您永远不会只是等待()。你需要一些条件来满足,如果没有,你就等待,直到它满足。所以你真正要做的是

if(!condition){
    wait();
}

但是这个条件是由一个单独的线程设置的,所以为了让它正确工作,你需要同步。

它还有一些错误的地方,仅仅因为你的线程退出等待并不意味着你正在寻找的条件是真的:

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

要处理这些情况,你真正需要的是:

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

更好的是,完全不要乱动同步原语,而是使用java.util.concurrent包中提供的抽象。

其他回答

我们都知道wait(), notify()和notifyAll()方法用于线程间 通信。为摆脱误信号和伪唤醒问题,等待线程 总是等待一些条件。 例如,

boolean wasNotified = false;
while(!wasNotified) {
    wait();
}

然后通知线程集wasNotified变量为true和notify。

每个线程都有自己的本地缓存,所以所有的更改都先写在那里 然后逐步提升到主存。

是否这些方法没有在同步块中调用,wasNotified变量 不会被刷新到主内存中,而是在线程的本地缓存中 因此,等待线程将继续等待信号,尽管它已通过通知重置 线程。

为了修复这些类型的问题,这些方法总是在同步块中调用 这确保当同步块开始时,那么一切将从主读取 内存,并将在退出同步块之前被刷新到主内存。

synchronized(monitor) {
    boolean wasNotified = false;
    while(!wasNotified) {
        wait();
    }
}

谢谢,希望它能澄清。

Thread wait on the monitoring object (object used by synchronization block), There can be n number of monitoring object in whole journey of a single thread. If Thread wait outside the synchronization block then there is no monitoring object and also other thread notify to access for the monitoring object, so how would the thread outside the synchronization block would know that it has been notified. This is also one of the reason that wait(), notify() and notifyAll() are in object class rather than thread class.

基本上监视对象在这里是所有线程的公共资源,监视对象只能在同步块中可用。

class A {
   int a = 0;
  //something......
  public void add() {
   synchronization(this) {
      //this is your monitoring object and thread has to wait to gain lock on **this**
       }
  }

@Rollerball是对的。调用wait(),以便线程可以等待某些条件的发生,当wait()调用发生时,线程被迫放弃它的锁。 要放弃一些东西,你需要先拥有它。线程首先需要拥有锁。 因此需要在同步方法/块中调用它。

是的,我同意上面所有关于潜在损害/不一致的答案,如果你没有检查同步方法/块中的条件。然而,正如@shrini1000所指出的,只是在同步块中调用wait()并不能避免这种不一致的发生。

这是一篇不错的文章。

当您在对象t上调用notify()时,Java会通知特定的t.wait()方法。但是,Java如何搜索并通知特定的等待方法呢?

Java只查看被对象t锁定的同步代码块。Java不能搜索整个代码来通知特定的t.wait()。

直接从这个Java oracle教程:

当线程调用d.wait时,它必须拥有d -的内在锁 否则抛出错误。在synchronized中调用等待 方法是一种获取内在锁的简单方法。