Java中long和double的原子性在java的基本类型中,long和double的长度都是8个字节,32位(4字节)的处理器无法一次完成其读写操作。那么,JVM是long和doubleatomic吗?JVM中对long的操作是原子操作吗?首先通过一个程序判断long的原子性。测试程序如下:publicclassLongAtomTestimplementsRunnable{privatestaticlongfield=0;privatevolatilelongvalue;publiclonggetValue(){返回值;}publicvoidsetValue(longvalue){this.value=value;}publicLongAtomTest(longvalue){this.setValue(value);}@Overridepublicvoidrun(){inti=0;while(i<100000){LongAtomTest.field=this.getValue();我++;longtemp=LongAtomTest.field;if(temp!=1L&&temp!=-1L){System.out.println("发生错误"+temp);系统.exit(0);}}System.out.println("正确运行");}publicstaticvoidmain(String[]args)throwsInterruptedException{//获取并打印当前JVM是32位还是64位Stringarch=System.getProperty("sun.arch.data.model");System.out.println(arch+"-bit");LongAtomTestt1=newLongAtomTest(1);LongAtomTestt2=newLongAtomTest(-1);线程T1=新线程(t1);线程T2=新线程(t2);T1.开始();T2.开始();T1.join();T2.join();}}可以看出程序中有两个线程t1和t2;t1和t2分别给long类型的静态变量字段赋值1和-1;每次赋值后,t1和t2会读取该字段的值,如果该字段的值既不是1也不是-1,则打印出该字段的值如果对long的写和读操作是原子的,则该值该字段只能为1或-1才能运行结果如下:32位有错误结果——4294967295运行正确。可以看出,当线程t1和t2同时写入long时,long既没有出现t1写入的值,也没有出现t2写入的值。可以推测,jvm中对long的操作不是原子操作。为什么long上的操作不是原子的?JVM内存模型定义了8个原子操作:1.lock:标记一个变量为线程独占2.unclock:释放独占状态的变量,释放后的变量可以被其他线程锁定3.read:传输将一个变量的值从主存复制到工作内存中,供后续加载操作使用4.load:把从主存读操作得到的变量值放到工作内存中的一个变量副本中5.use:把工作内存中变量的值被传递给执行引擎,每当虚拟机遇到使用该变量的指令时都会使用它。6.assign:将从执行引擎接收到的值赋给工作内存中的一个变量,每当虚拟机遇到给变量赋值的指令时,就必须使用这个操作7.store:传递一个变量的值8.write:从工作内存中调用store操作将内存中获取的变量值写入主存中的变量,赋值和赋值相关包括读取、加载、使用、分配、存储和写入。实践结果相反,为什么会出现这种问题呢?对于32位操作系统,单次操作所能处理的长度最长为32位,而long类型为8字节64位,所以读写long需要两条指令完成(即每次读并写入64位32位)。如果JVM要保证long和double读写的原子性,就必须做额外的处理。那么,JVM有对这种情况进行额外处理吗?针对这个问题可以参考Java语言规范文档:jls-17Non-AtomicTreatmentofdoubleandlong为了Java编程语言内存模型的目的,单次写入一个非易失性long或double值被视为两个单独的写入:每个32位一半。这可能导致线程从一次写入中看到64位值的前32位,而从另一次写入中看到第二个32位。volatilelong和double值的写入和读取始终是原子的。写入和读取引用的数量始终是原子的,无论它们是作为32位值还是64位值实现。某些实现可能会发现将64位长值或双精度值上的单个写操作分成两个相邻的32位写操作很方便-位值。为了效率起见,这种行为是特定于实现的;Java虚拟机的实现可以自由地以原子方式或分两部分执行对long和double值的写入。Implemen鼓励Java虚拟机的tations尽可能避免拆分64位值。鼓励程序员将共享的64位值声明为volatile或正确同步他们的程序,以避免可能出现的复杂情况。从规定中我们可以知道:1.对于64位的long和double如果没有被volatile修饰,那么对它们的操作就不必是原子的。操作的时候,可以分为两步,每一步操作都在32位上进行。2、如果用volatile修饰long和double,那么它的读写都是原子操作。3、对于64位引用地址的读写,都是原子操作。4、JVM实现时,可以自由选择是否读写long和doubleDouble作为原子操作5、建议JVM实现原子操作从程序得到的结果看,32位的HotSpot没有将long和double的读写实现为原子操作。读写时,分为两次操作,每次读写32位。因为这个策略,64位long和double的读写不是原子操作。如果硬件、操作系统和JVM都是64位的呢?对于64bit的环境,一次操作可以操作64bit的数据,即可以一次读写整个64bit的long或double。因此,我们可以猜测,在64位环境下,long和double的读写可能是原子操作。换了64位的JVM后,跑了很多次,结果都是正确的。64位操作正确。正确结果表明在64位虚拟机下,long的处理是原子的。作者|LouisWong图片来源|https://my.oschina.net/u/1753…
