众所周知,在进入同步块时,线程会重新读取其范围内的所有变量;也就是说,如果线程 A 在 RAM 中更新对象的状态,线程 B 进入同步块将看到更改。
第一部分是不正确的。不会重读所有变量。局部变量肯定不会。它们不需要重新阅读,因为它们永远不会被另一个线程看到。
正确的说法是编译器将确保线程 A在退出块之前写入的共享变量在线程 B进入块后对它是可见的。假设 A 和 B 在同一个互斥对象上同步,并且假设 A(或其他线程)在此期间没有覆盖它们。
没有与 anotify
或关联的显式内存语义notifyAll
。但是, await
将导致互斥锁被释放并(通常)重新获取。释放和重新获取与一些其他线程相关联的happens-before关系。
请您详细说明与释放和获取锁相关的确切语义吗?它们与进入同步块是否相同?
让我们假设我们只有两个线程,A 和 B,以及一个互斥锁 L。让我们假设我们从两个线程都没有持有互斥锁开始。
还要记住,wait
并且notify
只能由持有锁的线程调用
- 线程 A 获取 L。
- 线程 A 调用
L.wait()
.
- 线程 B 获取 L。
- 线程 B 调用
L.notify()
- 线程 B 释放 L。
- 线程 A 重新获取锁,
L.wait()
调用返回。
这里重要的发生前边缘在 2 和 3 之间,然后在 5 和 6 之间。
如果涉及多个线程,您可以通过链接发生之前的关系来分析行为。但是在释放互斥锁的线程和下一个获取它的线程之间只有一个直接的 HB ......无论如何。
因此,您的问题的答案是:
1) & 2) 是的,假设另一个线程使用synchronized
正确。3)不。可见点是互斥锁被调用的线程释放的时候notify()
。
请注意,内存屏障、刷新等是实现细节。事实上,编译器可以随意实现happens-before的语义。如果没有必要,包括(假设地)优化掉内存刷新。
最好(IMO)忽略这些实现细节,只考虑发生前的关系。
有关发生之前的更多信息,请阅读: