为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

慕村9548890 2019-07-01 14:54:44
为什么必须等待()总是处于同步块我们都知道为了调用Object.wait(),则必须将此调用放置在同步块中,否则IllegalMonitorStateException被扔了。但做这个限制的原因是什么?我知道wait()释放监视器,但是为什么我们需要通过使特定块同步,然后通过调用释放监视器来显式获取监视器?wait()?如果可以调用,那么潜在的损害是什么?wait()在同步块之外,保留它的语义-挂起调用线程?
查看完整描述

3 回答

?
慕慕森

TA贡献1856条经验 获得超17个赞

wait()只有当有一个notify()因此,它总是关于线程之间的通信,这需要同步才能正确工作。人们可能会争辩说,这应该是隐含的,但这并没有真正的帮助,原因如下:

在语义上,你从来没有wait()..你需要一些条件才能被饱和,如果不是,你就等到它达到饱和为止。所以你真正做的是

if(!condition){
    wait();}

但是条件是由一个单独的线程设置的,因此为了正确地完成这项工作,您需要同步。

它还有一些错误,仅仅因为您的线程停止等待并不意味着您正在寻找的条件是正确的:

  • 您可以得到虚假的唤醒(这意味着线程可以在没有收到通知的情况下从等待中醒来),或者

  • 条件可以设置,但是第三个线程在等待线程醒来时(并重新获取监视器)再次使条件变为假。

要处理这些案件,你真正需要的是这方面的一些变化:

synchronized(lock){
    while(!condition){
        lock.wait();
    }}

更好的是,根本不要处理同步原语,而要处理java.util.concurrent包裹。


查看完整回答
反对 回复 2019-07-01
?
浮云间

TA贡献1829条经验 获得超4个赞

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

让我们来说明一下如果wait()可以在同步块外部调用具体实例.

假设我们要实现一个阻塞队列(我知道,API中已经有一个:)

第一次尝试(没有同步)可以看到下面的一些内容

class BlockingQueue {
    Queue<String> buffer = new LinkedList<String>();

    public void give(String data) {
        buffer.add(data);
        notify();                   // Since someone may be waiting in take!
    }

    public String take() throws InterruptedException {
        while (buffer.isEmpty())    // don't use "if" due to spurious wakeups.
            wait();
        return buffer.remove();
    }}

这就是可能发生的情况:

  1. 使用者线程调用take()看到buffer.isEmpty().

  2. 在使用者线程继续调用之前wait(),就会出现一个生产者线程并调用一个完整的线程。give()就是,buffer.add(data); notify();

  3. 使用者线程现在将调用wait()(和小姐这个notify()刚刚打电话)。

  4. 如果不幸,生产者线程不会产生更多give()由于消费者线程永远不会醒来,我们就有了死锁。

一旦您理解了这个问题,解决方案就很明显了:synchronized确保notify从来没有在isEmptywait.

没有详细说明:这个同步问题是普遍的。正如MichaelBorgwardt所指出的,等待/通知是线程之间通信的全部内容,因此始终会出现类似于上面所述的争用状态。这就是为什么“仅在同步中等待”规则被强制执行的原因。


@Willie发布的链接总结得很好:

您需要一个绝对的保证,使服务员和通知者就谓词的状态达成一致。服务生在谓词进入睡眠前的某个点稍微检查它的状态,但它取决于谓词在进入睡眠时是否为真。这两个事件之间存在一段时间的漏洞,可能会破坏程序。

生产者和消费者需要商定的谓词在上面的示例中。buffer.isEmpty()..通过确保等待和通知在synchronized街区。


这篇文章被改写为一篇文章:Java:为什么必须在同步块中调用等待


查看完整回答
反对 回复 2019-07-01
?
当年话下

TA贡献1890条经验 获得超9个赞

是对的。这个wait()调用,以便线程可以在以下情况下等待发生某些条件:wait()调用发生时,线程被迫放弃其锁。
要放弃某些东西,你必须先拥有它。线程需要先拥有锁。因此,需要在synchronized方法/块

是的,我同意上述所有关于潜在损害/不一致的答案,如果你不检查里面的情况的话。synchronized方法/块然而,正如@S收缩1000所指出的,只是打电话wait()在同步块中不能避免这种不一致性的发生。

这是一本很好的读物.。


查看完整回答
反对 回复 2019-07-01
  • 3 回答
  • 0 关注
  • 379 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信