这个64位数字变量多线程规则在Java 1.8中是否仍然适用,或者它是否已过时?

Ada*_*dam 5 java memory multithreading low-latency

来自Goetz,Peierls,Bloch等.2006:实践中的Java并发

3.1.2.非原子64位操作

当一个线程在没有同步的情况下读取一个变量时,它可能会看到一个陈旧的值,但至少它会看到某个线程实际放置的值而不是某个随机值.这种安全保证称为超薄空气安全.

超薄空气安全适用于所有变量,但有一个例外:未声明为volatile的64位数字变量(double和long)(参见第3.1.4节).Java内存模型要求获取和存储操作是原子操作,但对于非易失性长和双变量,JVM允许将64位读或写视为两个独立的32位操作.如果读取和写入发生在不同的线程中,则可以读取非易失性long并返回一个值的高32位和另一个值的低32位.[3]

因此,即使您不关心陈旧值,在多线程程序中使用共享的可变长和双变量也是不安全的,除非它们被声明为volatile或由锁保护.

[3] 当编写Java虚拟机规范时,许多广泛使用的处理器体系结构无法有效地提供原子64位算术运算.

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

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

能够编写多线程和低延迟应用程序而不用担心这个问题是不是有益处?

kro*_*lko 6

这个64位数字变量多线程规则在Java 1.8中是否仍然适用,或者它是否已过时?

是的,它仍然是真的,参考Java 8规范:https:
//docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7

出于Java编程语言内存模型的目的,对非易失性long或double值的单次写入被视为两个单独的写入:每个32位半写一次.这可能导致线程从一次写入看到64位值的前32位,而从另一次写入看到第二次32位的情况.

volatile和long值的写入和读取始终是原子的.

无论是否将它们实现为32位或64位值,对引用的写入和读取始终是原子的.


那为什么它仍然适用?

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

某些实现可能会发现将64位长或双值上的单个写操作划分为相邻32位值上的两个写操作很方便.为了效率,这种行为是特定于实现的; Java虚拟机的实现可以自由地以原子方式或分两部分执行对long和double值的写入.

鼓励Java虚拟机的实现避免在可能的情况下拆分64位值.建议程序员将共享的64位值声明为volatile或正确同步其程序以避免可能的复杂情况.

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