前言
JVM中对long的操作是不是原子操作?
双精度实数,占用字节8。有效数字15~16(因为二进制转十进制问题)。数值范围-1.7*10负308次方~1.7*10的308次方。当有效数字全部是整数时,即整数有15~16位。C语言中,输出double类型(双精度实型)以及float类型(单精度。
为什么对long的操作不是原子的?
在硬件,操作系统,JVM都是64位的情况下呢?
java中基本类型中,long和double的长度都是8个字节,32位(4字节)处理器对其读写操作无法一次完成,那么,JVM,long和double是原子性的吗?
JVM中对long的操作是不是原子操作?
8个。double是C语言的双精度浮点类型,对于其占字节数,C语言规范中,并没有明确规定其所占空间,只是要求double类型的整数部分的最小表示范围为1.0E-37到 1.0E+37,小数部分至少要能精确到小数点后10位。所以在实现中。
首先,通过一段程序对long的原子性进行判断。测试程序如下:
数据类型double占几个字节,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运行正确
为什么对long的操作不是原子的?
JVM内存模型中定义了8中原子操作:
lock:将一个变量标识为被一个线程独占状态
unclock:将一个变量从独占状态释放出来,释放后的变量才可以被其他线程锁定
read:将一个变量的值从主内存传输到工作内存中,以便随后的load操作
load:把read操作从主内存中得到的变量值放入工作内存的变量的副本中
use:把工作内存中的一个变量的值传给执行引擎,每当虚拟机遇到一个使用到变量的指令时都会使用该指令
c语言中double(双精度浮点型)是计算机使用的一种数据类型。double(双精度浮点型)使用 64 位(8字节) 来储存一个浮点数。 它可以表示十进制的15或16位有效数字,其数值范围为-1.79769313486232E308 到1.
store:把工作内存中的一个变量的值传递给主内存,以便随后的write操作
write:把store操作从工作内存中得到的变量的值写到主内存中的变量
其中,与赋值,取值相关的包括 read,load,use,assign,store,write
按照这个规定,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.
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,那么其读写都是原子操作
double占8个字节(64位)存空间,最多可提供16位有效数字,小数点后默认保留6位。如全是整数,最多提供15位有效数字。学习,是指通过阅读、听讲、思考、研究、实践等途径获得知识和技能的过程。学习分为狭义与广义两种:狭义。
对于64位的引用地址的读写,都是原子操作
在实现JVM时,可以自由选择是否把读写long和double作为原子操作
推荐JVM实现为原子操作
从程序得到的结果来看,32位的HotSpot没有把long和double的读写实现为原子操作。在读写的时候,分成两次操作,每次读写32位。因为采用了这种策略,所以64位的long和double的读与写都不是原子操作。
在硬件,操作系统,JVM都是64位的情况下呢?
64-bit运行正确运行正确
结果表明,在64bit的虚拟机下,long的处理是原子性的。