如果我有这样的代码 -
long x;
x = 0xFFFFFFFFL;
如果我在32位机器上运行此代码,它是否保证是原子的,或者读取x的不同线程是否可能得到不完整/垃圾值?
答案 0 :(得分:47)
以下是简短摘要:
int
,char
,byte
,short
,boolean
,float
,读/写始终 atomic double
和long
,如果它们是volatile
,则读取/写入始终原子因此,只有读/写可能不是原子的异常:
double
和long
,如果他们 NOT 宣布为volatile
,则他们 NOT GUARANTEED 是原子的因此,就读/写共享数据的原子性而言,您只需要volatile
任意double
或long
。无论在实际实现中使用了多少位,其他所有内容都已保证是原子的。
以下是此处转载的相关部分,以供快速参考:
JLS 17.7 Non-atomic Treatment of
double
andlong
某些实现可能会发现将64位
long
或double
值上的单个写操作划分为相邻32位值的两个写操作很方便。为了效率,这种行为是特定于实现的; Java虚拟机可以原子地或分两部分对long
和double
值进行写入。出于Java编程语言内存模型的目的,对非
volatile long
或double
值的单个写入被视为两个单独的写入:每个32位半写一个。这可能导致线程从一次写入看到64位值的前32位,而从另一次写入看到第二次32位的情况。volatile long
和double
值的写入和读取始终是原子的。对引用的写入和读取始终是原子的,无论它们是实现为32位还是64位值。鼓励VM实现者尽可能避免拆分64位值。建议程序员将共享的64位值声明为
volatile
或正确同步其程序以避免可能的复杂情况。
byte
可以在没有邻居干扰的情况下更新volatile
Fields int i;
,i++
NOT 原子!volatile long[]
volatile
引用到long
long
元素本身不是volatile
答案 1 :(得分:11)
不,他们不是。 64位存储被视为两个独立的32位存储。因此,在并发环境中,变量可以具有一个写入的高32和另一个写入的低32,显然不是线程安全的。
答案 2 :(得分:9)
Java虚拟机规范的第8.4节规定,未声明为volatile的double或long被视为两个32位变量,用于加载,存储,读取和写入操作。
此外,未定义编码方式和两个32位操作的顺序。
规范确实鼓励实现使操作成为原子,但它们不需要它。
答案 3 :(得分:8)
如果变量是volatile
,则读/写保证是原子的,但如果变量是非易失性的则不是。
某些实现可能会发现划分单个写操作很方便 动作为64位长或双 值为两个写操作 相邻的32位值。对于 效率的缘故,这种行为是 具体实施; Java虚拟 机器可以自由执行写入 原子或长的双重值 分两部分。
出于Java编程语言内存模型的目的,a 单次写入非易失性长或 double值被视为两个 单独写入:每个32位一个 半。这可能导致一种情况 线程看到前32位的位置 一次写入的64位值,和 另一个写入的第二个32位。 写入和读取volatile和long double值总是原子的。
JLS 17.7 - Non-atomic Treatment of double and long
当多个线程在没有同步的情况下访问long值时,必须使用volatile
来确保一个线程所做的更改对其他线程可见,并确保读/写是原子的。