假设我知道我将在 x64 cpus 上运行,我可以忽略哪些 JVM 同步实践?
我知道 JVM 内存模型是为 CPU 的最小公分母而设计的,因此它必须假设 JVM 可以在其上运行的 CPU 的最弱模型(例如 ARM).
I know that the JVM memory model is made for lowest common denominator of CPUs, so it has to assume the weakest possible model of a cpu on which the JVM can run (eg ARM).
现在,考虑到 x64 具有相当强大的内存模型,假设我知道我的程序只能在 64 位 x86 CPU 上运行,我可以忽略哪些同步做法?当我的程序通过虚拟化运行时,这也适用吗?
Now, considering that x64 has a fairly strong memory model, what synchronization practices can I ignore assuming I know my program will only run on 64bit x86 CPUs? Also does this apply when my program is being run through virtualization?
示例:
众所周知,JVM 的内存模型需要同步对 long 和 double 的读/写访问,但可以假设其他 32 位原语(如 int、float 等)的读/写是原子的.
Example:
It is known that JVM's memory model requires synchronizing read/writes access to longs and doubles but one can assume that read/writes of other 32 bit primitives like int, float etc are atomic.
但是,如果我知道我在 64 位 x86 机器上运行,我是否可以忽略在 longs/doubles 上使用锁,因为我知道 cpu 将自动读取/写入 64 位值并保持它们易失性(就像我会整数/浮点数)?
However, if i know that I am running on a 64 bit x86 machine, can i ignore using locks on longs/doubles knowing that the cpu will atomically read/write 64 bit values and just keep them volatile (like i would with ints/floats)?
推荐答案
我知道 JVM 内存模型是为 CPU 的最小公分母而设计的,因此它必须假设 JVM 可以在其上运行的 CPU 的最弱模型(例如 ARM).
I know that the JVM memory model is made for lowest common denominator of CPUs, so it has to assume the weakest possible model of a cpu on which the JVM can run (eg ARM).
这是不正确的.JMM 源于多种竞争力量之间的妥协:需要较弱的内存模型,以便程序可以在内存模型较弱的硬件上运行得更快;希望允许某些优化的编译器编写者的愿望;并且希望并行 Java 程序的结果是正确和可预测的,并且如果可能(!)Java 程序员可以理解.请参阅 Sarita Adve 的 CACM文章了解内存模型问题的一般概述.
That's not correct. The JMM resulted from a compromise among a variety of competing forces: the desire for a weaker memory model so that programs can go faster on hardware that have weak memory models; the desire of compiler writers who want certain optimizations to be allowed; and the desire for the result of parallel Java programs to be correct and predictable, and if possible(!) understandable to Java programmers. See Sarita Adve's CACM article for a general overview of memory model issues.
考虑到 x64 具有相当强大的内存模型,假设我知道我的程序只能在 [x64] CPU 上运行,我可以忽略哪些同步做法?
Considering that x64 has a fairly strong memory model, what synchronization practices can I ignore assuming I know my program will only run on [x64] CPUs?
没有.问题在于,内存模型不仅适用于底层硬件,还适用于执行程序的 JVM,实际上主要是 JVM 的 JIT 编译器.编译器可能会决定应用内存模型中允许的某些优化,但如果您的程序基于底层硬件对内存行为做出无根据的假设,您的程序将会中断.
None. The issue is that the memory model applies not only to the underlying hardware, but it also applies to the JVM that's executing your program, and mostly in practice, the JVM's JIT compiler. The compiler might decide to apply certain optimizations that are allowed within the memory model, but if your program is making unwarranted assumptions about the memory behavior based on the underlying hardware, your program will break.
您询问了 x64 和原子 64 位写入.在 x64 机器上可能永远不会发生单词撕裂.我怀疑任何 JIT 编译器都会将 64 位值撕成 32 位写入作为优化,但你永远不知道.但是,您似乎不太可能使用此功能来避免程序中的同步或易失字段.如果没有这些,对这些变量的写入可能永远不会对其他线程可见,或者它们可能相对于其他写入任意重新排序,可能导致程序中的错误.
You asked about x64 and atomic 64-bit writes. It may be that no word tearing will ever occur on an x64 machine. I doubt that any JIT compiler would tear a 64-bit value into 32-bit writes as an optimization, but you never know. However, it seems unlikely that you could use this feature to avoid synchronization or volatile fields in your program. Without these, writes to these variables might never become visible to other threads, or they could arbitrarily be re-ordered with respect to other writes, possibly leading to bugs in your program.
我的建议是首先正确应用同步以使您的程序正确.你可能会感到惊喜.同步操作已经过大量优化,在常见情况下可以非常快.如果您发现存在瓶颈,请考虑使用锁拆分、使用 volatile 或转换为非阻塞算法等优化.
My advice is first to apply synchronization properly to get your program correct. You might be pleasantly surprised. The synchronization operations have been heavily optimized and can be very fast in the common case. If you find there are bottlenecks, consider using optimizations like lock splitting, the use of volatiles, or converting to non-blocking algorithms.
更新
OP 已将问题更新为更具体地使用 volatile
而不是锁和同步.
The OP has updated the question to be a bit more specific about using volatile
instead of locks and synchronization.
事实证明,volatile
不仅具有内存可见性语义.它还使 long
和 double
访问原子,这不是这些类型的非 volatile
变量的情况.请参阅 JLS 部分 17.7.您应该能够依靠 volatile
在任何硬件上提供原子性,而不仅仅是 x64.
It turns out that volatile
not only has memory visibility semantics. It also makes long
and double
access atomic, which is not the case for non-volatile
variables of those types. See the JLS section 17.7. You should be able to rely on volatile
to provide atomicity on any hardware, not just x64.
关于 Java 内存模型的更多信息,请参阅 Aleksey Shipilev 的 JMM Pragmatics 谈话记录.(Aleksey 也是 JMH 人.)这次谈话有很多细节,还有一些有趣的练习来测试一个人的理解力.演讲的一个总体结论是,依靠自己对记忆模型如何工作的直觉通常是错误的,例如在高速缓存行或写缓冲区方面.JMM 是关于内存操作的形式主义,以及决定这些操作顺序的各种约束(synchronizes-with、happens-before 等).这可能会产生非常违反直觉的结果.试图通过考虑特定的硬件属性来超越 JMM 是不明智的.它会回来咬你的.
While I'm at it, for additional information about the Java Memory Model, see Aleksey Shipilev's JMM Pragmatics talk transcript. (Aleksey is also the JMH guy.) There's lots of detail in this talk, and some interesting exercises to test one's understanding. One overall takeaway of the talk is that it's often a mistake to rely on one's intuition about how the memory model works, e.g. in terms of cache lines or write buffers. The JMM is a formalism about memory operations and various contraints (synchronizes-with, happens-before, etc.) that determine ordering of those operations. This can have quite counterintuitive results. It's unwise to try to outsmart the JMM by thinking about specific hardware properties. It'll come back to bite you.
相关文章