先從一段代碼開始
private static AtomicInteger a = new AtomicInteger(0);
private static int anInt = 0;
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length ; i++) {
threads[i] = new Thread(()->{
for (int j = 0; j < 1000; j++) {
a.incrementAndGet();
anInt++;
}
});
threads[i].start();
}
Thread.sleep(100);
System.out.println("原子類:"+a.get());
System.out.println("基本數(shù)據(jù)類型int:"+anInt);
}
其結(jié)果如下
原子類:100000
基本數(shù)據(jù)類型int:63516
原子了每次運(yùn)行的結(jié)果都能達(dá)到預(yù)期谜诫,而int基本無法達(dá)到。
查看incrementAndGet()方法源碼
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
定位到Unsafe.class
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
在回寫到內(nèi)存時(shí)膊存,比較期望值,若發(fā)生改變則重新讀取變量再執(zhí)行一次操作。這即是比較并交換的思想:CAS (compareAndSwap)
那么在這個(gè)過程中必然涉及的兩個(gè)問題:
1荷荤、期望值會(huì)不會(huì)是多個(gè)線程多次回寫的結(jié)果退渗,導(dǎo)致期望值相等?
2蕴纳、在判斷期望值相等的一瞬間氓辣,會(huì)不會(huì)有線程將變量的值修改?
對(duì)于問題一袱蚓,加上版本號(hào)即可解決钞啸。至于問題二,則需要查看C++代碼喇潘,最終在C++代碼中找到實(shí)現(xiàn)
lock cmpxchg 指令
lock指令在執(zhí)行后面指令的時(shí)候鎖定一個(gè)北橋信號(hào)体斩,任何線程都得等我執(zhí)行完才能執(zhí)行,這樣就保證了CAS的原子性