为什么必须等待()总是处于同步块

为什么必须等待()总是处于同步块

我们都知道为了调用Object.wait(),则必须将此调用放置在同步块中,否则IllegalMonitorStateException被扔了。但做这个限制的原因是什么?我知道wait()释放监视器,但是为什么我们需要通过使特定块同步,然后通过调用释放监视器来显式获取监视器?wait()?

如果可以调用,那么潜在的损害是什么?wait()在同步块之外,保留它的语义-挂起调用线程?



慕村9548890
浏览 380回答 3
3回答

慕慕森

A wait()只有当有一个notify()因此,它总是关于线程之间的通信,这需要同步才能正确工作。人们可能会争辩说,这应该是隐含的,但这并没有真正的帮助,原因如下:在语义上,你从来没有wait()..你需要一些条件才能被饱和,如果不是,你就等到它达到饱和为止。所以你真正做的是if(!condition){     wait();}但是条件是由一个单独的线程设置的,因此为了正确地完成这项工作,您需要同步。它还有一些错误,仅仅因为您的线程停止等待并不意味着您正在寻找的条件是正确的:您可以得到虚假的唤醒(这意味着线程可以在没有收到通知的情况下从等待中醒来),或者条件可以设置,但是第三个线程在等待线程醒来时(并重新获取监视器)再次使条件变为假。要处理这些案件,你真正需要的是总这方面的一些变化:synchronized(lock){     while(!condition){         lock.wait();     }}更好的是,根本不要处理同步原语,而要处理java.util.concurrent包裹。

浮云间

如果可以调用,那么潜在的损害是什么?wait()在同步块之外,保留它的语义-挂起调用线程?让我们来说明一下如果wait()可以在同步块外部调用具体实例.假设我们要实现一个阻塞队列(我知道,API中已经有一个:)第一次尝试(没有同步)可以看到下面的一些内容class&nbsp;BlockingQueue&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;Queue<String>&nbsp;buffer&nbsp;=&nbsp;new&nbsp;LinkedList<String>(); &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;void&nbsp;give(String&nbsp;data)&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;buffer.add(data); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;notify();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;Since&nbsp;someone&nbsp;may&nbsp;be&nbsp;waiting&nbsp;in&nbsp;take! &nbsp;&nbsp;&nbsp;&nbsp;} &nbsp;&nbsp;&nbsp;&nbsp;public&nbsp;String&nbsp;take()&nbsp;throws&nbsp;InterruptedException&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;while&nbsp;(buffer.isEmpty())&nbsp;&nbsp;&nbsp;&nbsp;//&nbsp;don't&nbsp;use&nbsp;"if"&nbsp;due&nbsp;to&nbsp;spurious&nbsp;wakeups. &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;wait(); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return&nbsp;buffer.remove(); &nbsp;&nbsp;&nbsp;&nbsp;}}这就是可能发生的情况:使用者线程调用take()看到buffer.isEmpty().在使用者线程继续调用之前wait(),就会出现一个生产者线程并调用一个完整的线程。give()就是,buffer.add(data); notify();使用者线程现在将调用wait()(和小姐这个notify()刚刚打电话)。如果不幸,生产者线程不会产生更多give()由于消费者线程永远不会醒来,我们就有了死锁。一旦您理解了这个问题,解决方案就很明显了:synchronized确保notify从来没有在isEmpty和wait.没有详细说明:这个同步问题是普遍的。正如MichaelBorgwardt所指出的,等待/通知是线程之间通信的全部内容,因此始终会出现类似于上面所述的争用状态。这就是为什么“仅在同步中等待”规则被强制执行的原因。从@Willie发布的链接总结得很好:您需要一个绝对的保证,使服务员和通知者就谓词的状态达成一致。服务生在谓词进入睡眠前的某个点稍微检查它的状态,但它取决于谓词在进入睡眠时是否为真。这两个事件之间存在一段时间的漏洞,可能会破坏程序。生产者和消费者需要商定的谓词在上面的示例中。buffer.isEmpty()..通过确保等待和通知在synchronized街区。这篇文章被改写为一篇文章:Java:为什么必须在同步块中调用等待

当年话下

是对的。这个wait()调用,以便线程可以在以下情况下等待发生某些条件:wait()调用发生时,线程被迫放弃其锁。要放弃某些东西,你必须先拥有它。线程需要先拥有锁。因此,需要在synchronized方法/块是的,我同意上述所有关于潜在损害/不一致的答案,如果你不检查里面的情况的话。synchronized方法/块然而,正如@S收缩1000所指出的,只是打电话wait()在同步块中不能避免这种不一致性的发生。这是一本很好的读物.。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java