为什么所有Java对象都有wait()和notify(),这会导致性能下降吗?

StackOverflow https://stackoverflow.com/questions/1648551

  •  22-07-2019
  •  | 
  •  

每个爪哇 Object 有方法 wait()notify() (以及其他变体)。我从未使用过这些,我怀疑许多其他人也没有使用过。为什么这些如此重要以至于每个对象都必须拥有它们,并且拥有它们是否会对性能造成影响(大概其中存储了一些状态)?

编辑 来强调这个问题。如果我有一个 List<Double> 有 100,000 个元素,则每个 Double 有这些方法,因为它是从 Object. 。但似乎不太可能所有这些都必须知道管理 List.

编辑 优秀且有用的答案。@Jon 有一篇非常好的博客文章,它具体化了我的直觉。我也完全同意@Bob_Cross 的观点,即您应该在担心性能问题之前先显示它。(也作为成功语言的第 n 条法则,如果它对性能造成了影响,那么 Sun 或其他人就会修复它)。

有帮助吗?

解决方案

好了,这确实意味着每个对象具有潜在具有与其相关联的监视器。在同一个显示器用于synchronized。如果你有决定不服的,以便能够在任何对象上同步,然后wait()notify()不添加任何更多的每个对象的状态。该JVM可以懒洋洋地分配实际监视器(我知道.NET一样),但必须有可用的说哪个监视器与对象相关的一些存储空间。诚然,这是可能的,这是一个非常小的量(例如3个字节),这实际上不会无论如何保存任何内存由于对象的开销,其余的填充 - 你必须看看如何每一个人JVM处理内存的说肯定的。

请注意,仅仅具有额外的方法不会影响性能(比非常轻微其它由于代码明显存在的某处)。它不象每一个对象,甚至每一种类型都有自己的代码的副本wait()notify()。根据虚函数表是如何工作的,每种类型的可能的最终为每个继承的方法一个额外的V表项 - 但是这仍然只是在每个类型的基础上,而不是每个对象的基础。这基本上要迷失在噪声与本体,其是实际对象本身存储的比较。

就个人而言,我觉得这两个.NET和Java通过监控每对象关联犯了一个错误 - 我宁愿有明确的同步对象,而不是。我写了一点更多关于这在博客文章关于重新设计java.lang.Object中/ System.Object的

其他提示

为什么这些都是如此的基本,以至于每个对象都必须拥有它们,并且在拥有它们时会受到绩效的打击(大概某些状态存储在其中)?

长话短说:它们是线程安全方法,相对于它们的价值而言,它们的成本很小。

基本现实是 这些方法 支持的是:

  1. Java 始终是多线程的。例子:有时使用 jconsole 或 jvisualvm 检查进程使用的线程列表。
  2. 正确性比“性能”更重要。当我进行分级项目(很多年前)时,我曾经不得不解释“找到错误的答案 真的很快 还是错了。”

从根本上讲,这些方法提供了一些挂钩来管理同步中使用的每个对象监视器。具体来说,如果我有 synchronized(objectWithMonitor) 在特定的方法中,我可以使用 objectWithMonitor.wait() 产生该监视器(例如,如果我需要另一种方法来完成计算才能继续)。在这种情况下,这将允许被阻止等待该监视器继续进行的另一种方法。

另一方面,我可以使用 objectWithMonitor.notifyAll() 让正在等待监视器的线程知道我将很快放弃监视器。不过,在我离开同步块之前,它们实际上无法继续。

对于您可能担心监控机制会出现性能或内存问题的特定示例(例如,长双精度列表),您可能应该考虑以下几点:

  1. 首先,证明一下。如果您认为核心 Java 机制(例如多线程正确性)会产生重大影响,那么您的直觉很可能是错误的。首先衡量影响。如果情况很严重并且你 知道 您永远不需要在单个 Double 上进行同步,请考虑使用 Double 来代替。
  2. 如果你不确定你、你的同事、未来的维护编码员(一年后可能就是你自己)等,永远不会 曾经 如果您需要对数据进行细粒度的主题访问,那么取消这些监视器很可能只会降低您的代码的灵活性和可维护性。

后续回答有关每个对象与单个对象的问题显式监控对象:

简短回答: @乔恩斯基特:是的,移除显示器会产生问题:它会产生摩擦。将这些显示器保留在 Object 提醒我们这是 总是 多线程系统。

内置对象监视器并不复杂,但它们是:容易解释;以可预测的方式工作;并且目的明确。 synchronized(this) 是一个明确的意图声明。如果我们强迫新手编码人员专门使用并发包,就会带来摩擦。那个包裹里有什么?什么是信号量?分叉连接?

新手编码人员可以使用对象监视器编写不错的模型-视图-控制器代码。 synchronized, waitnotifyAll 可用于实现简单(简单、可访问但可能不是前沿性能)线程安全。典型的例子是这些 Double 之一(由 OP 提出),它可以让一个线程设置一个值,而 AWT 线程获取该值并将其放在 JLabel 上。在这种情况下,没有充分的理由创建一个显式的附加对象只是为了拥有一个外部监视器。

在复杂性稍高的情况下,这些相同的方法可用作外部监测方法。在上面的示例中,我明确地这样做了(请参阅上面的 objectWithMonitor 片段)。同样,这些方法对于组合相对简单的线程安全性确实非常方便。

如果你想变得更复杂,我认为你应该认真考虑阅读 Java 并发实践 (如果你还没有)。读锁和写锁非常强大,而且不会增加太多额外的复杂性。

妙处: 使用基本的同步方法,您可以充分利用现代多核处理器所提供的大部分性能,并且具有线程安全性,并且无需大量开销。

Java中的所有对象都有一个与之相关联的显示器。同步原语是在几乎所有的多线程代码,而且它的语义非常高兴您正在访问,而不是单独的“监控”对象的对象(或多个)上同步。

根据需要的Java可以分配与对象相关联的显示器 - 如.NET那样 - 和在任何情况下用于简单地分配(但不使用)锁定将是非常小的实际开销

在短:它真的很方便与他们线程安全的支持位存储对象,并且很少有表现的影响

这些方法都是为了实现线程间通信。

查看 这篇关于这个主题的文章.

这些方法的规则摘自该文章:

  • wait()告诉通话线程放弃监视器并入睡,直到其他一些线程进入同一监视器并呼叫Notify()。
  • notify() 唤醒对同一对象调用 wait() 的第一个线程。
  • notifyAll() 唤醒对同一对象调用 wait() 的所有线程。最高优先级线程将首先运行。

希望这可以帮助...

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top