java - 这个 64 位数字变量多线程规则在 Java 1.8 中仍然适用还是已经过时了?

标签 java multithreading memory low-latency

来自 Goetz、Peierls、Bloch 等人。 2006 年:Java 并发实践

3.1.2. Nonatomic 64-bit Operations

When a thread reads a variable without synchronization, it may see a stale value, but at least it sees a value that was actually placed there by some thread rather than some random value. This safety guarantee is called out-of-thin-air safety.

Out-of-thin-air safety applies to all variables, with one exception: 64-bit numeric variables ( double and long ) that are not declared volatile (see Section 3.1.4 ). The Java Memory Model requires fetch and store operations to be atomic, but for nonvolatile long and double variables, the JVM is permitted to treat a 64-bit read or write as two separate 32-bit operations. If the reads and writes occur in different threads, it is therefore possible to read a nonvolatile long and get back the high 32 bits of one value and the low 32 bits of another.[3]

Thus, even if you don't care about stale values, it is not safe to use shared mutable long and double variables in multithreaded programs unless they are declared volatile or guarded by a lock.

[3] When the Java Virtual Machine Specification was written, many widely used processor architectures could not efficiently provide atomic 64-bit arithmetic operations.

这是在 2004 年发布的 Java 5 之后编写的,其中有许多针对更容易的多线程和并发编程的更改。那为什么它仍然适用呢?即使是十年后的现在?

如果只是因为可以在 32 位硬件上运行 Java 应用程序,为什么没有 JVM 运行时选项来允许它(如果需要)?

如果能够编写多线程和低延迟应用程序而无需担心这个陷阱,这不是很有好处吗?

最佳答案

Is this 64-bit numeric variable multi-threading rule still true in Java 1.8 or is it out-of-date?

是的,还是这样,引用Java 8 Specification:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7

For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64-bit value from one write, and the second 32 bits from another write.

Writes and reads of volatile long and double values are always atomic.

Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.


So why does it still apply?

规范中也有说明:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7

Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32-bit values. For efficiency's sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perform writes to long and double values atomically or in two parts.

Implementations of the Java Virtual Machine are encouraged to avoid splitting 64-bit values where possible. Programmers are encouraged to declare shared 64-bit values as volatile or synchronize their programs correctly to avoid possible complications.

我认为这是因为 Java 运行在许多硬件模型上——例如手机、路由器,甚至可能在冰箱、洗衣机、真空吸尘器等上,它们可以配备微型 8 位或 16 位微处理器。 Java 规范对所有这些规范都是通用的。

关于java - 这个 64 位数字变量多线程规则在 Java 1.8 中仍然适用还是已经过时了?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37226361/

相关文章:

java - 使用 Netbeans 表单时出现通用 "Link Error"

python - Flask session 变量是否跨线程维护状态?

c - 总线错误: 10 when scanning address space in c

c++ - 由于某种原因,我的 ram 上的 int 重量超过 32 位

c - 如何调试 C 中的内存覆盖?

java - Android aws CognitoUserSession.getIdToken() 给出空指针异常

java - Spring 的链式 react

java - 为什么 System.getProperty ("user.name");无法从命令行运行?

java - Java的ThreadPoolExecutor如何使用自定义ThreadFactory?

C++ - 将数据复制到 2 个不同线程的最快/可靠方法