为啥在 Java 的 Object 类中声明 wait() 和 notify()?

Posted

技术标签:

【中文标题】为啥在 Java 的 Object 类中声明 wait() 和 notify()?【英文标题】:Why are wait() and notify() declared in Java's Object class?为什么在 Java 的 Object 类中声明 wait() 和 notify()? 【发布时间】:2010-12-18 16:13:22 【问题描述】:

为什么wait()notify() 方法声明在Object 类中,而不是Thread 类中?

【问题讨论】:

【参考方案1】:

因为,您要等待给定的对象(或具体来说,它的监视器)才能使用此功能。

我认为您可能误解了这些方法的工作原理。它们不只是处于线程粒度级别,即不是只是调用wait() 并被下一次调用notify() 唤醒的情况。相反,您总是在特定对象上调用 wait(),并且只会通过在该对象上调用 notify 来唤醒。

这很好,否则并发原语将无法扩展;这相当于拥有全局命名空间,因为在程序中的任何位置对notify() 的任何调用都可能会弄乱任何并发代码,因为它们会唤醒阻塞在wait() 上的任何线程称呼。因此,您在特定对象上调用它们的原因;它为等待通知对操作提供了上下文,因此当您在私有对象上调用myBlockingObject.notify() 时,您可以确定您只会唤醒类中调用等待方法的线程。某些可能正在等待另一个对象的 Spring 线程不会被此调用唤醒,反之亦然。

编辑:或者从另一个角度解决它 - 我希望从你的问题中你认为你会得到一个等待线程的句柄并在 那个线程 上调用 notify() 来唤醒它。不这样做的原因是你必须自己做很多家务。等待的线程必须在其他线程可以看到的地方发布对自身的引用;这必须适当地同步以加强一致性和可见性。当你想唤醒一个线程时,你必须获取这个引用,唤醒它,然后从你阅读它的任何地方删除它。与仅在睡眠线程中调用 myObj.wait() 然后在唤醒线程中调用 myObj.notify() 相比,涉及更多的手动脚手架,并且出错的可能性更大(尤其是在并发环境中)。

【讨论】:

这并没有开始回答为什么您可以等待任何对象。为什么没有特定的 Lock 类或类型?或者可能是一个标记界面? "...可能会弄乱任何并发代码,因为它们会唤醒任何阻塞在 wait() 调用上的线程...”只要“搞砸”意味着“限制表现。”它实际上不应该破坏任何东西,因为一个 always 在循环中调用 wait() 直到一个正在等待的条件变为 true。 @mjaggard 我认为 Solomon 的回答回答了这个问题:***.com/a/34293039/731752。它归结为“它没有必要。”【参考方案2】:

最简单和最明显的原因是任何对象(不仅仅是一个线程) 可以是线程的监视器。等待和通知在 监视器。正在运行的线程检查监视器。所以wait和notify方法在Object而不是Thread

【讨论】:

【参考方案3】:

因为一次只有一个线程可以拥有一个对象的监视器,而这个监视器是线程正在等待或通知的。如果您阅读了javadoc 中的Object.notify()Object.wait(),则会对其进行详细描述。

【讨论】:

【参考方案4】:

同步机制涉及一个概念——对象的监视器。当调用 wait() 时,将请求监视器并暂停进一步的执行,直到获取监视器或发生 InterruptedException。当调用 notify() 时,释放监视器。

假设wait() 和notify() 被放置在Thread 类而不是Object 类中。在代码中的某一时刻,currentThread.wait() 被调用,然后对象 anObject 被访问。

//.........
currentThread.wait();
anObject.setValue(1);
//.........

当 currentThread.wait() 被调用时,currentThread 的监视器被请求,并且在获取监视器或发生 InterruptedException 之前不会进一步执行。现在处于等待状态时,如果从另一个线程调用驻留在currentThread 中的另一个对象anotherObject 的方法foo(),即使被调用的方法foo() 没有访问anObject,它也会卡住。如果在anObject 上调用第一个wait() 方法,而不是线程本身,则对驻留在同一线程中的对象的其他方法调用(不访问anObject)不会卡住。

因此在 Object 类(或其子类)上调用 wait() 和 notify() 方法提供了更高的并发性,这就是为什么这些方法在 Object 类中,而不是在 Thread 类中。

【讨论】:

【参考方案5】:

其他一些答案使用“监视器”一词,但没有人解释它的含义。

“监视器”这个名称早在 1970 年代就被创造出来了,它指的是一个拥有自己的内在锁和相关等待/通知机制的对象。 https://en.wikipedia.org/wiki/Monitor_%28synchronization%29

20 年后,有一个短暂的时刻,台式机、多处理器计算机是新的,流行的想法是为它们设计软件的正确方法是创建面向对象的程序,其中 每个物体都是一个监视器。

事实证明这不是一个有用的想法,但那个短暂的时刻恰好是 Java 编程语言被发明的时候。

【讨论】:

【参考方案6】:

阅读 here 了解等待和通知的说明。

最好在您的应用程序中避免这些,并使用更新的java.util.concurrent 包。

【讨论】:

【参考方案7】:

我会简单地说:

要调用 wait() 或 notify(),您需要拥有对象监视器 - 这意味着 wait() 或 notify() 需要存在于同步块中

synchronized(monitorObj)
monitorObj.wait()  or even notify

这就是这些方法存在于对象类中的原因

【讨论】:

【参考方案8】:

这是因为,这些方法是用于线程间通信的,线程间通信是通过锁来实现的,但是锁是与对象相关联的,所以它在对象类中。

【讨论】:

【参考方案9】:

Wait 和 Notify 方法用于 Java 中两个线程之间的通信。所以 Object 类是让它们可用于 Java 中的每个对象的正确位置。

另一个原因是每个对象都可以使用锁。线程需要锁,他们等待锁,他们不知道哪些线程持有锁,他们只知道锁被某个线程持有,他们应该等待锁,而不是知道哪个线程在同步块内并要求他们释放锁

【讨论】:

以上是关于为啥在 Java 的 Object 类中声明 wait() 和 notify()?的主要内容,如果未能解决你的问题,请参考以下文章

scala设计者为啥要提供package object

为啥可以在java的抽象类中声明瞬态变量? [复制]

为啥 main 应该出现在 Java 类中? [复制]

为啥在 JDK 类中使用完全限定名称声明 Serializable?

为啥 Object.clone() 在 Java 中是原生的?

为啥我们不能在一个类中声明一个命名空间?