wait() 和 notify() JMM 语义

wyd*_*e19 1 java multithreading synchronized java-memory-model happens-before

我有一个非常特别的问题,我找不到答案。

正如我们所知,在synchronized块的入口处,线程会重新读取其范围内的所有共享(非本地)变量。某些底层架构的示例:如果线程 A 更新 RAM 中的对象状态,线程 B 进入同步块将看到更改。类似的事情发生在退出synchronized块时:线程将其范围内的所有内容刷新到 RAM,以便其他线程可以看到它。这些是基本的 JVM 可见性保证,并且存在发生之前的规则来强制执行它。

但是,从语义上讲,代码使用wait()notify()也执行所有这些操作并不是很清楚:毕竟它没有明确地进入或离开synchronized块。

这些问题是:

  1. JVM 是否确保在wait()入口处对其他线程的更改可见?
  2. JVM 是否确保其他线程在wait()休假时所做更改的可见性?
  3. 该线程是否确保对其他线程的更改可见 notify()

Ste*_*n C 6

我们知道,在同步块的入口处,线程会重新读取其作用域内的所有变量;也就是说,如果线程 A 更新 RAM 中的对象状态,线程 B 进入同步块将看到更改。

第一部分是不正确的。不会重新读取所有变量。局部变量肯定不会。他们并不需要重新读取,因为他们从来到另一个线程可见。

正确的说法是编译器将确保线程 A退出块之前写入的共享变量在线程 B进入块可见。假设 A 和 B 在同一个互斥对象上同步,并且 A(或其他线程)在此期间没有覆盖它们。

没有与 anotify或相关联的显式内存语义notifyAll。但是, await将导致互斥体被释放和(通常)重新获取。释放和重新获取与其他线程相关联的happens-before关系。


您能否详细说明与释放和获取锁相关的确切语义?它们是否与进入同步块相同?

让我们假设我们只有两个线程,A 和 B,以及一个互斥锁 L。让我们假设我们开始时没有一个线程持有互斥锁。

还要记住,wait并且notify只能由持有锁的线程调用

  1. 线程 A 获取 L。
  2. 线程 A 调用L.wait().
    • 线程 A 被放置在 L 的等待队列中,并且 L 被释放。
  3. 线程 B 获取 L。
  4. 线程 B 调用 L.notify()
    • 线程 A 在等待获取 L 的线程队列中移动。
  5. 线程 B 释放 L。
  6. 线程 A 重新获取锁,L.wait()调用返回。

此处重要的发生前边在 2 到 3 之间,然后在 5 到 6 之间。

如果涉及多个线程,您可以通过链接发生之前的关系来分析行为。但是在释放互斥锁的线程和下一个获取它的线程之间只有一个直接的 HB ......无论如何。


所以,你的问题的答案是:

1) & 2) 是的,假设另一个线程使用synchronized正确。3)否。可见点是互斥锁被调用的线程释放的时间notify()


请注意,内存屏障、刷新等是实现细节。事实上,编译器可以自由地以任何它想要的方式实现happens-before的语义。如果没有必要,包括(假设)优化掉内存刷新。

最好 (IMO) 忽略这些实现细节,考虑发生之前的关系。

有关发生之前的更多信息,请阅读: