是否在32位机器上使用Java原子进行64位分配?

pde*_*eva 27 java concurrency multithreading

如果我有这样的代码 -

long x;
x  = 0xFFFFFFFFL;
Run Code Online (Sandbox Code Playgroud)


如果我在32位机器上运行此代码,它是否保证是原子的,或者读取x的不同线程是否可能获得不完整/垃圾值?

pol*_*nts 48

以下是简短摘要:

  • 对于引用,读/写总是原子的(即使在64位实现中!)
  • 对于int,char,byte,short,boolean,float,读/写操作总是原子
  • 对于doublelong,如果它们是volatile,读/写总是原子的

因此,只有读/写可能不是原子的异常:

  • 对于doublelong,如果他们没有被宣布volatile,他们不保证是原子的

因此,就读/写共享数据的原子性而言,您只需要制作volatile任何doublelong.无论在实际实现中使用了多少位,其他所有内容都已保证是原子的.


在规范上

以下是此处转载的相关部分,以供快速参考:

JLS 17.7非原子化处理doublelong

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

出于Java编程语言内存模型的目的,对non volatile longdoublevalue 的单个写入被视为两个单独的写入:每个32位半写一个.这可能导致线程从一次写入看到64位值的前32位,而从另一次写入看到第二次32位的情况.写入和读取volatile long以及double值始终是原子的.对引用的写入和读取始终是原子的,无论它们是实现为32位还是64位值.

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

也可以看看

相关问题

  • 这个答案似乎暗示你永远不需要例如`volatile int`.事实并非如此.易失性也用于建立先发生过的关系; 共享非易失性`int`的原子性只能保证其他线程永远不会看到垃圾值.除非你使用`volatile int`,否则其他线程永远不会看到更新的值是合法的(如果不太可能). (5认同)
  • 似乎javadoc假定一个运行JVM的32位系统.如果一个64位JVM在64位系统上运行,那么64位的long和double的读/写可以是原子的,至少在理论上是这样的,对吧?我知道我们仍然应该正确地编写代码以使其向后兼容32位系统,这意味着只要使用32位系统,我们就必须编写最坏情况的代码. (2认同)

Joh*_*int 11

不,他们不是.64位存储被视为两个独立的32位存储.因此,在并发环境中,变量可以具有一个写入的高32和另一个写入的低32,显然不是线程安全的.


Rya*_*per 9

Java虚拟机规范的第8.4节规定,未声明为volatile的double或long被视为两个32位变量,用于加载,存储,读取和写入操作.

此外,没有定义编码方式和两个32位操作的顺序.

该规范确实鼓励实现使操作成为原子,但它们并不需要它.


mdm*_*dma 8

如果变量是volatile,那么读/写保证是原子的,但如果变量是非易失性的则不是.

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

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

JLS 17.7 - 双原子和非原子的非原子化处理

当多个线程在没有同步的情况下访问long值时,必须使用volatile以确保一个线程所做的更改对其他线程可见,并确保读/写是原子的.