【问题标题】:Why must wait and notify be called from synchronized block/method? [duplicate]为什么必须从同步块/方法中调用等待和通知? [复制]
【发布时间】:2014-01-02 13:02:37
【问题描述】:

在我正在阅读的书中说:

由于存在竞争条件,因此需要此技术,否则 在设置和发送通知和测试之间存在 收到通知。如果 wait() 和 notify() 机制是 没有在持有同步锁的时候调用,就不会有 保证收到通知的方法。

不明白这到底是什么意思,为什么会出现竞态条件?

编辑:嗯,我现在看到这可能是 Why must wait() always be in synchronized block 的重复问题 ,但答案似乎集中在使条件检查和等待同步上。

来自 shrini1000 的反例:

我仍然可以这样做:
while(!condition) { synchronized(this) { wait(); } }
这意味着在检查条件和等待之间仍然存在竞争 如果在同步块中正确调用了 wait()。有没有 此限制背后的任何其他原因,可能是由于它的方式 用Java实现?

【问题讨论】:

  • 我认为答案不存在,原因是我的问题中的反例。
  • 这里没有反例。您的 wait() 调用位于同步块中。
  • 其根本原因是内存模型的工作方式 - 您可以查看:docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.2,了解等待集如何工作的详细说明。
  • 没有完全找到我要找的东西,但是对于链接 +1
  • 有些东西可能被误用的事实并不是一个反例。

标签: java multithreading concurrency wait notify


【解决方案1】:

这一定是关于作者在您复制的文章之前必须展示的技术。我不确定您正在阅读哪本书,但我会尝试回答这个问题。

我读过一本类似的书“Thinking in Java”,其中谈到了相同的竞争条件。它表明可以使用等待和通知来防止这种情况,这样代码就不会错过通知信号。

当两个线程使用 notify( )/wait( ) 或 notifyAll( )/wait( ),可能会错过一个信号。假设 T1 是一个线程 通知 T2,并且这两个线程是使用 以下(有缺陷的)方法:

T1:

synchronized(sharedMonitor) {
    <setup condition for T2>
    sharedMonitor.notify();
}

T2:

while(someCondition) {
    // Assume that T2 evaluates someCondition and finds 
    // it true, now when program goes to next line thread
    // scheduler switches to T1 and executes notify again
    // after when control comes to T2 it blindly executes
    // wait(), but it has already missed notify so it will
    // always be waiting.

    .... some code ....

    synchronized(sharedMonitor) {
        sharedMonitor.wait();
    }
}

(T2 的设置条件)是防止 T2 调用 wait() 的操作,如果它尚未调用。

解决方案是防止 someCondition 变量上的竞争条件。以下是 T2 的正确方法:

synchronized(sharedMonitor) {
    while(someCondition) {
        sharedMonitor.wait();
    }
}

【讨论】:

  • 是的,看了一会我觉得这本书的作者说的是这个案子。它是“Java 线程,第 3 版”。 O'Reilly,我不推荐。
  • 希望我已经回答了你的问题。好吧,您可以在 java 中进行思考(只需阅读并发章节就足以了解线程),更多详细信息 jcip 是最好的。
  • 是的,你做到了,我想你回答了作者的意图,但这不完全是我的问题。无论如何,我的问题并不重要。关于 jcip 我有,我会在阅读当前的书后阅读它。我会给你投票,但不是标记,原因;)
【解决方案2】:

某些东西可能被滥用这一事实几乎不是反例。

Java 仅强制 wait()notify() 是同步块的一部分(因为这是它们应该被使用的唯一方式),但由您来定义块边界。

作为一个反例,想想finally 块。 Java 只强制它出现在 try 块之后,但您是唯一应该知道应该进入该 try 块的人;您甚至可以将其留空(这样会错过finally 的重点)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-10
    • 2011-10-24
    • 2015-01-26
    • 2012-11-23
    • 2014-10-23
    • 2019-10-09
    • 1970-01-01
    • 2023-04-06
    相关资源
    最近更新 更多