共计 3483 个字符,预计需要花费 9 分钟才能阅读完成。
作者:LouisWong
起源:https://my.oschina.net/u/1753415/blog/724242
Java 中 long 和 double 的原子性
java 中根本类型中,long 和 double 的长度都是 8 个字节,32 位(4 字节)处理器对其读写操作无奈一次实现,那么,JVM,long 和 double 是原子性的吗?
JVM 中对 long 的操作是不是原子操作?
首先,通过一段程序对 long 的原子性进行判断。测试程序如下:
public class LongAtomTest implements Runnable {
private static long field = 0;
private volatile long value;
public long getValue() {return value;}
public void setValue(long value) {this.value = value;}
public LongAtomTest(long value) {this.setValue(value);
}
@Override
public void run() {
int i = 0;
while (i < 100000) {LongAtomTest.field = this.getValue();
i++;
long temp = LongAtomTest.field;
if (temp != 1L && temp != -1L) {System.out.println("呈现谬误后果" + temp);
System.exit(0);
}
}
System.out.println("运行正确");
}
public static void main(String[] args) throws InterruptedException {
// 获取并打印以后 JVM 是 32 位还是 64 位的
String arch = System.getProperty("sun.arch.data.model");
System.out.println(arch+"-bit");
LongAtomTest t1 = new LongAtomTest(1);
LongAtomTest t2 = new LongAtomTest(-1);
Thread T1 = new Thread(t1);
Thread T2 = new Thread(t2);
T1.start();
T2.start();
T1.join();
T2.join();}
}
能够看到,程序中有两条线程 t1,t2;t1,t2 各自不停的给 long 类型的动态变量 field 赋值为 1,-1;t1,t2 每次赋值后,会读取 field 的值,若 field 值既不是 1 又不是 -1,就将 field 的值打印进去
如果对 long 的写入和读取操作是原子性的,那么,field 的值只可能是 1 或者 -1
运行后果如下
32-bit
呈现谬误后果 -4294967295
运行正确
能够看出,当线程 t1,t2 同时对 long 进行写的时候,long 呈现了既不是 t1 写入的值,又不是 t2 写入的值。能够揣测,jvm 中对 long 的操作并非原子操作。
为什么对 long 的操作不是原子的?
JVM 内存模型中定义了 8 中原子操作:
- lock: 将一个变量标识为被一个线程独占状态
- unclock: 将一个变量从独占状态释放出来,开释后的变量才能够被其余线程锁定
- read: 将一个变量的值从主内存传输到工作内存中,以便随后的 load 操作
- load: 把 read 操作从主内存中失去的变量值放入工作内存的变量的正本中
- use: 把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个应用到变量的指令时都会应用该指令
- assign: 把一个从执行引擎接管到的值赋给工作内存中的变量,每当虚拟机遇到一个给变量赋值的指令时,都要应用该操作
- store: 把工作内存中的一个变量的值传递给主内存,以便随后的 write 操作
- write: 把 store 操作从工作内存中失去的变量的值写到主内存中的变量
其中,与赋值,取值相干的包含 read,load,use,assign,store,write
依照这个规定,long 的读写都是原子操作,与咱们的实际后果相同,为什会导致这种问题呢?
对于 32 位操作系统来说,单次次操作能解决的最长长度为 32bit,而 long 类型 8 字节 64bit,所以对 long 的读写都要两条指令能力实现(即每次读写 64bit 中的 32bit)。如果 JVM 要保障 long 和 double 读写的原子性,势必要做额定的解决。那么,JVM 有对这一状况进行额定解决吗?针对这一问题能够参考 Java 语言标准文档:jls-17 Non-Atomic Treatment of double and long
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.
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.
从规定中咱们能够晓得:
- 对于 64 位的 long 和 double,如果没有被 volatile 润饰,那么对其操作能够不是原子的。在操作的时候,能够分成两步,每次对 32 位操作。
- 如果应用 volatile 润饰 long 和 double,那么其读写都是原子操作
- 对于 64 位的援用地址的读写,都是原子操作
- 在实现 JVM 时,能够自由选择是否把读写 long 和 double 作为原子操作
- 举荐 JVM 实现为原子操作
从程序失去的后果来看,32 位的 HotSpot 没有把 long 和 double 的读写实现为原子操作。在读写的时候,分成两次操作,每次读写 32 位。因为采纳了这种策略,所以 64 位的 long 和 double 的读与写都不是原子操作。
在硬件,操作系统,JVM 都是 64 位的状况下呢?
对于 64bit 的环境来说,单次操作能够操作 64bit 的数据,即能够以一次性读写 long 或 double 的整个 64bit。因而咱们能够猜想,在 64 位的环境下,long 和 double 的读写有可能是原子操作。在换了 64 位的 JVM 之后,屡次运行,后果都是正确的
64-bit
运行正确
运行正确
结果表明,在 64bit 的虚拟机下,long 的解决是原子性的。
近期热文举荐:
1.1,000+ 道 Java 面试题及答案整顿 (2021 最新版)
2. 终于靠开源我的项目弄到 IntelliJ IDEA 激活码了,真香!
3. 阿里 Mock 工具正式开源,干掉市面上所有 Mock 工具!
4.Spring Cloud 2020.0.0 正式公布,全新颠覆性版本!
5.《Java 开发手册(嵩山版)》最新公布,速速下载!
感觉不错,别忘了顺手点赞 + 转发哦!