Java中的原子操做包括:java
1)除long和double以外的基本類型的賦值操做eclipse
2)全部引用reference的賦值操做ide
3)java.concurrent.Atomic.* 包中全部類的一切操做。this
可是java對long和double的賦值操做是非原子操做!!long和double佔用的字節數都是8,也就是64bits。在32位操做系統上對64位的數據的讀寫要分兩步完成,每一步取32位數據。這樣對double和long的賦值操做就會有問題:若是有兩個線程同時寫一個變量內存,一個進程寫低32位,而另外一個寫高32位,這樣將致使獲取的64位數據是失效的數據。所以須要使用volatile關鍵字來防止此類現象。volatile自己不保證獲取和設置操做的原子性,僅僅保持修改的可見性。可是java的內存模型保證聲明爲volatile的long和double變量的get和set操做是原子的。(from http://www.iteye.com/topic/213794)atom
舉個例子來講:(example is from http://stackoverflow.com/questions/17481153/long-and-double-assignments-are-not-atomic-how-does-it-matter)spa
public class UnatomicLong implements Runnable {操作系統
private static long test = 0;.net
private final long val;線程
public UnatomicLong(long val) {進程
this.val = val;
}
public void run() {
while (!Thread.interrupted()) {
test = val; //兩個線程都試圖將本身的私有變量val賦值給類私有靜態變量test
}
}
public static void main(String[] args) {
Thread t1 = new Thread(new UnatomicLong(-1));
Thread t2 = new Thread(new UnatomicLong(0));
System.out.println(Long.toBinaryString(-1));
System.out.println(pad(Long.toBinaryString(0), 64));
t1.start();
t2.start();
long val;
while ((val = test) == -1 || val == 0) {
//若是靜態成員test的值是-1或0,說明兩個線程操做沒有交叉
}
System.out.println(pad(Long.toBinaryString(val), 64));
System.out.println(val);
t1.interrupt();
t2.interrupt();
}
// prepend 0s to the string to make it the target length
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;
}
}
運行發現程序在while循環時進入了死循環,這是由於使用的JVM是64bits。在64位JVM中double和long的賦值操做是原子操做。
在eclipse中修改jre爲一個32bit的JVM地址,則會有以下運行結果:
1111111111111111111111111111111111111111111111111111111111111111
0000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000011111111111111111111111111111111
//很明顯test的值被破壞了
4294967295