为什么这些如此重要以至于每个对象都必须拥有它们,并且拥有它们是否会影响性能(可能某些状态存储在它们中)?
tl;dr:它们是线程安全方法,相对于它们的价值而言,它们的成本很小。
这些方法支持的基本现实是:
- Java 总是多线程的。示例:查看某个使用 jconsole 或 jvisualvm 的进程使用的线程列表。
- 正确性比“性能”更重要。当我为项目评分时(很多年前),我曾经不得不解释“很快得到错误的答案仍然是错误的”。
从根本上说,这些方法提供了一些挂钩来管理同步中使用的每个对象的监视器。具体来说,如果我有synchronized(objectWithMonitor)
一个特定的方法,我可以使用objectWithMonitor.wait()
来生成那个监视器(例如,如果我需要另一种方法来完成计算,然后才能继续进行)。在这种情况下,这将允许另一种方法被阻止等待该监视器继续进行。
另一方面,我可以objectWithMonitor.notifyAll()
让等待监视器的线程知道我将很快放弃监视器。不过,在我离开同步块之前,它们实际上无法继续。
对于您可能担心监控机制会影响性能或内存的特定示例(例如,长的 Doubles 列表),您可能应该考虑以下几点:
- 首先,证明它。如果您认为核心 Java 机制(例如多线程正确性)会产生重大影响,那么您的直觉很有可能是错误的。首先衡量影响。如果它很严重,并且您知道您永远不需要在单个 Double 上同步,请考虑使用双精度。
- 如果您不确定您、您的同事、未来的维护编码员(可能在一年后成为您自己)等永远不会需要对您的数据进行精细访问,这是一个很好的机会拿走这些监视器只会使您的代码不那么灵活和可维护。
针对每个对象与显式监控对象的问题的后续行动:
简短回答: @JonSkeet:是的,移除监视器会产生问题:它会产生摩擦。保留这些监视器Object
提醒我们,这始终是一个多线程系统。
内置对象监视器并不复杂,但它们: 易于解释;以可预测的方式工作;并且他们的目的很明确。 synchronized(this)
是明确的意向声明。如果我们强制新手编码员专门使用并发包,我们就会引入摩擦。那个包裹里有什么?什么是信号量?分叉加入?
新手程序员可以使用对象监视器来编写体面的模型-视图-控制器代码。 synchronized
,wait
并且notifyAll
可以用来实现简单的(在简单、可访问但可能不是前沿性能的意义上)线程安全。典型示例是这些 Doubles 之一(由 OP 提出),它可以让一个 Thread 设置一个值,而 AWT 线程获取该值以将其放在 JLabel 上。在这种情况下,没有充分的理由仅仅为了拥有一个外部监视器而创建一个显式的附加对象。
在复杂程度稍高的情况下,这些相同的方法可用作外部监控方法。在上面的示例中,我明确地这样做了(参见上面的 objectWithMonitor 片段)。同样,这些方法对于组合相对简单的线程安全非常方便。
如果您想变得更复杂,我认为您应该认真考虑阅读Java Concurrency In Practice(如果您还没有的话)。读写锁非常强大,不会增加太多额外的复杂性。
Punchline:使用基本的同步方法,您可以利用现代多核处理器所提供的大部分性能,具有线程安全且没有大量开销。