我最近正在阅读《破解代码访谈》这本书,但第 257 页有一段让我很困惑:
线程是进程的特定执行路径;当一个线程修改进程资源时,该更改立即对兄弟线程可见。
IIRC,如果一个线程对变量进行更改,更改将首先保存在 CPU 缓存(例如 L1 缓存)中,并且除非变量声明为 volatile,否则不会保证与其他线程同步。
我对吗?
我最近正在阅读《破解代码访谈》这本书,但第 257 页有一段让我很困惑:
线程是进程的特定执行路径;当一个线程修改进程资源时,该更改立即对兄弟线程可见。
IIRC,如果一个线程对变量进行更改,更改将首先保存在 CPU 缓存(例如 L1 缓存)中,并且除非变量声明为 volatile,否则不会保证与其他线程同步。
我对吗?
不,你错了。但这是一个非常普遍的误解。
每个现代多核 CPU 都具有硬件缓存一致性。L1 和类似的缓存是不可见的。像 L1 缓存这样的 CPU 缓存与内存可见性无关。
当线程修改进程资源时,更改立即可见。问题在于优化导致进程资源不能精确地按照代码指定的顺序进行修改。
如果您的代码具有k = j; i = 4; if (j == 2) foo();
优化器,则可能会看到您的第一个分配读取j
. 因此,当您将其与它进行比较时,它可能不会再阅读它,2
因为它“知道”它不能改变。但是,另一个线程可能已经改变了它。因此,当需要线程之间的同步时,需要禁用某些类型的优化。这就是喜欢volatile
做的事情。
如果编译器和 CPU 不进行优化并按照编写的程序精确执行程序,则volatile
永远不需要。内存可见性是关于代码优化(一些由编译器完成,一些由 CPU 完成),而不是缓存。
我认为您引用的文字不正确。Java 内存模型的整个想法是处理现代软件和硬件的复杂优化,以便程序员可以确定其他线程中的相应读取可以看到哪些写入。
除非 Java 程序正确同步,否则您不能保证一个线程的更改立即对其他线程可见。也许文本指的是一个非常具体(和弱)的记忆模型。
使用 volatile 变量只是线程同步的一种方式,并不适合所有场景。
- 编辑 -
我想我现在理解了这种困惑......我同意大卫施瓦茨的观点,假设:
1)“修改进程资源”是指资源的实际更改,而不仅仅是执行以某种高级计算机语言编写的写指令。
2)“对兄弟线程立即可见”意味着其他线程能够看到它;这并不意味着程序中的线程一定会看到它。您可能仍需要使用同步工具来禁用绕过对资源的实际访问的优化。