我们都知道为了调用Object.wait(),这个调用必须放在同步块中,否则抛出IllegalMonitorStateException。但是为什么要做出这样的限制呢?我知道wait()释放监视器,但为什么我们需要通过使特定块同步显式获取监视器,然后通过调用wait()释放监视器?
如果可以在同步块之外调用wait(),保留它的语义——挂起调用者线程,那么潜在的损害是什么?
我们都知道为了调用Object.wait(),这个调用必须放在同步块中,否则抛出IllegalMonitorStateException。但是为什么要做出这样的限制呢?我知道wait()释放监视器,但为什么我们需要通过使特定块同步显式获取监视器,然后通过调用wait()释放监视器?
如果可以在同步块之外调用wait(),保留它的语义——挂起调用者线程,那么潜在的损害是什么?
当前回答
根据文档:
当前线程必须拥有该对象的监视器。线程释放 这个监视器的所有权。
Wait()方法仅仅意味着它释放对象上的锁。因此,对象只在同步块/方法中被锁定。如果线程在同步块之外意味着它没有被锁定,如果它没有被锁定那么你会释放对象上的什么?
其他回答
这基本上与硬件架构(即RAM和缓存)有关。
如果你没有将synchronized与wait()或notify()一起使用,另一个线程可能会进入同一个块,而不是等待监视器进入它。此外,当访问一个没有同步块的数组时,另一个线程可能看不到它的变化…实际上,当另一个线程已经在处理CPU内核的x级缓存(也就是1 /2 /3级缓存)中有一个数组的副本时,它不会看到对它的任何更改。
但是同步块只是奖章的一面:如果您实际上从非同步上下文中访问同步上下文中的对象,即使在同步块中,该对象仍然不会被同步,因为它在其缓存中保存了对象的自己副本。我在这里写了这个问题:https://stackoverflow.com/a/21462631和当一个锁持有一个非final对象时,对象的引用仍然可以被另一个线程改变吗?
此外,我确信x级缓存要对大多数不可重现的运行时错误负责。这是因为开发人员通常不了解底层的东西,比如CPU如何工作或内存层次结构如何影响应用程序的运行:http://en.wikipedia.org/wiki/Memory_hierarchy
为什么编程类不首先从内存层次结构和CPU架构开始,这仍然是一个谜。“Hello world”在这里没用。;)
当您在对象t上调用notify()时,Java会通知特定的t.wait()方法。但是,Java如何搜索并通知特定的等待方法呢?
Java只查看被对象t锁定的同步代码块。Java不能搜索整个代码来通知特定的t.wait()。
根据文档:
当前线程必须拥有该对象的监视器。线程释放 这个监视器的所有权。
Wait()方法仅仅意味着它释放对象上的锁。因此,对象只在同步块/方法中被锁定。如果线程在同步块之外意味着它没有被锁定,如果它没有被锁定那么你会释放对象上的什么?
@Rollerball是对的。调用wait(),以便线程可以等待某些条件的发生,当wait()调用发生时,线程被迫放弃它的锁。 要放弃一些东西,你需要先拥有它。线程首先需要拥有锁。 因此需要在同步方法/块中调用它。
是的,我同意上面所有关于潜在损害/不一致的答案,如果你没有检查同步方法/块中的条件。然而,正如@shrini1000所指出的,只是在同步块中调用wait()并不能避免这种不一致的发生。
这是一篇不错的文章。
直接从这个Java oracle教程:
当线程调用d.wait时,它必须拥有d -的内在锁 否则抛出错误。在synchronized中调用等待 方法是一种获取内在锁的简单方法。