41

工作中的某个人只是询问必须在同步中包含等待的原因。

老实说,我看不出其中的道理。我理解 javadocs 所说的——线程需要是对象监视器的所有者,但为什么呢?它防止了哪些问题?(如果确实有必要,为什么等待方法不能获取监视器本身?)

我正在寻找一个相当深入的原因,或者可能是对一篇文章的参考。我在快速谷歌中找不到一个。

哦,还有,thread.sleep 比较如何?

编辑:一组很棒的答案——我真的希望我能选择多个答案,因为它们都帮助我理解了发生了什么。

4

6 回答 6

14

这里已经有很多好的答案了。但是在这里只想提一下,使用 wait() 时必须做的另一个事情是根据您等待的条件在循环中执行它,以防您看到虚假唤醒,根据我的经验,这确实发生了。

等待其他线程将条件更改为 true 并通知:

synchronized(o) {
  while(! checkCondition()) {
    o.wait();
  }
}

当然,这些天来,我建议只使用新的 Condition 对象,因为它更清晰并且具有更多功能(例如每个锁允许多个条件,能够检查等待队列长度,更灵活的调度/中断等)。

 Lock lock = new ReentrantLock();
 Condition condition = lock.newCondition();
 lock.lock();
 try {
   while (! checkCondition()) {
     condition.await();
   }
 } finally {
   lock.unlock();
 }

}

于 2008-10-22T18:58:45.637 回答
6

如果对象在调用 Object.wait() 时不拥有对象监视器,则在释放监视器之前,它将无法访问该对象以设置通知侦听器。相反,它将被视为尝试访问同步对象上的方法的线程。

或者换句话说,两者之间没有区别:

public void doStuffOnThisObject()

和以下方法:

public void wait()

这两种方法都将被阻止,直到对象监视器被释放。这是 Java 中的一项功能,用于防止对象的状态被多个线程更新。它只是对 wait() 方法产生了意想不到的后果。

据推测,wait() 方法是不同步的,因为这可能会导致线程在对象上有多个锁的情况。(有关这方面的更多信息,请参阅Java 语言规范/锁定。)多个锁是一个问题,因为 wait() 方法只会撤消一个锁。如果方法是同步的,它将保证只有方法的锁会被撤消,而潜在的外部锁仍然会被撤消。这将在代码中创建死锁条件。

要回答您关于 Thread.sleep() 的问题,Thread.sleep() 不保证您等待的任何条件都已满足。使用 Object.wait() 和 Object.notify() 允许程序员手动实现阻塞。一旦发送满足条件的通知,线程将解除阻塞。例如,从磁盘读取已完成,线程可以处理数据。Thread.sleep() 将要求程序员轮询条件是否满足,如果没有满足则返回睡眠。

于 2008-10-22T16:03:09.530 回答
5

它需要拥有监视器,因为 wait() 的目的是释放监视器,让其他线程获得监视器进行自己的处理。这些方法(等待/通知)的目的是协调对需要彼此执行某些功能的两个线程之间的同步代码块的访问。这不仅仅是确保对数据结构的访问是线程安全的,而是协调多个线程之间的事件。

一个典型的例子是生产者/消费者案例,其中一个线程将数据推送到队列,而另一个线程消费数据。消费线程总是需要监视器访问队列,但一旦队列为空就会释放监视器。只有当消费者不再进行处理时,生产者线程才能获得对该线程的写入权限。一旦将更多数据推送到队列中,它将通知消费者线程,因此它可以重新获得监视器并再次访问队列。

于 2008-10-22T16:40:47.130 回答
5

等待放弃显示器,所以你必须拥有它才能放弃它。通知也必须有监视器。

您想要这样做的主要原因是确保当您从 wait() 回来时拥有监视器——通常,您使用等待/通知协议来保护某些共享资源,并且您希望它是安全的等待返回时触摸它。与 notify 相同——通常你正在更改某些内容然后调用 notify()——你想要监控、进行更改并调用 notify()。

如果你做了这样的功能:

public void synchWait() {
   syncronized { wait(); }
}

当等待返回时,您将没有监视器——您可以得到它,但接下来您可能不会得到它。

于 2008-10-22T16:46:07.400 回答
3

这是我对为什么限制实际上是一项要求的理解。我将此基于我不久前通过组合互斥锁和条件变量所做的 C++ 监视器实现。

mutex+condition_variable=monitor系统中,等待调用将条件变量设置为等待状态并释放互斥锁。条件变量是共享状态,因此需要锁定它以避免在想要等待的线程和想要通知的线程之间出现竞争条件。不是引入另一个互斥锁来锁定其状态,而是使用现有的互斥锁。在 Java 中,当即将等待的线程拥有监视器时,互斥锁被正确锁定。

于 2008-10-22T16:53:17.463 回答
3

如果有条件说队列为空,则通常等待完成。

If(queue is empty)
     queue.wait();

让我们假设队列是空的。如果当前线程在检查队列后抢占,那么如果另一个线程向队列中添加了很少的元素,则当前线程将不知道并进入等待状态。那是错误的。所以我们应该有类似的东西

Synchornized(queue)
{
   if(queue is empty)
          queue.wait();
}

现在让我们考虑一下如果他们让 wait 本身同步会怎样。正如其中一条评论中已经提到的,它只释放一个锁。这意味着如果 wait() 在上面的代码中同步,只有一个锁会被释放。暗示当前线程将等待队列的锁。

于 2009-07-23T05:33:16.420 回答