In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn’t happen at all. No side effects of an atomic action are visible until the action is complete.
以上是关于原子性的操作的相关描述。
在Java中,以下的操作可以认为是原子操作
对于引用变量、大多数的原始类型变量的读、写(所有的类型除了long和double)都是原子性的(这个只有才32位的JVM成立)
所有申明为volatile的变量(包括long和double变量)的读、写都是原子性的
在Java中,自增、自减操作都不是原子性操作,很容易理解。但是long和duoble的读写却不是原子性的问题,却不太好理解。
检验 在32位JVM上,通过两个线程对同一个成员变量进行读写,来测试long和double类型变量的读写不是原子性操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 public class UnatomicLongDemo implements Runnable { private static long test = 0 ; private final long val; public UnatomicLongDemo (long val) { this .val = val; } @Override public void run () { while (!Thread.interrupted()) { test = val; } } public static void main (String[] args) { Thread t1 = new Thread (new UnatomicLongDemo (-1 )); Thread t2 = new Thread (new UnatomicLongDemo (0 )); System.out.println(Long.toBinaryString(-1 )); System.out.println(pad(Long.toBinaryString(0 ), 64 )); t1.start(); t2.start(); long switchVal; while ((switchVal = test) == -1 || switchVal == 0 ) { System.out.println("testing..." ); } System.out.println(pad(Long.toBinaryString(switchVal), 64 )); System.out.println(switchVal); t1.interrupt(); t2.interrupt(); } private static String pad (String s, int targetLength) { int n = targetLength - s.length(); for (int x = 0 ; x < n; x++) { s = "0" + s; } return s; } }
测试结果为
通过这个例子可以看出
在32位JVM上,对long型变量test的读取或者对long型变量switchVal的写入不是原子性的。
非原子性的读、写只是造成long、double类型变量的高、低32位数据不一致
这是由于在32位JVM中对64位的数据的读、写分两步,每一步读或者写32位的数据,这样就会造成两个线程对同一个变量的读写出现一个线程写高32位、另一个线程写入低32位数据。这样此变量的数据就出现不一致的情况。这时候volatile关键字可以防止这种现象发生,因为java的内存模型保证了valotile修饰的long、double变量的读写是原子性的。