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
变量的读写是原子性的。