像synchronized
這種獨占鎖屬于悲觀鎖,它是在假設一定會發生沖突的,那么加鎖恰好有用,除此之外,還有樂觀鎖,樂觀鎖的含義就是假設沒有發生沖突,那么我正好可以進行某項操作,如果要是發生沖突呢,那我就重試直到成功,樂觀鎖最常見的就是CAS
。
我們在讀Concurrent包下的類的源碼時,發現無論是ReenterLock內部的AQS,還是各種Atomic開頭的原子類,內部都應用到了CAS
,最常見的就是我們在并發編程時遇到的i++
這種情況。傳統的方法肯定是在方法上加上synchronized
關鍵字:
public class Test {public volatile int i;public synchronized void add() {i++;}
}
復制代碼
但是這種方法在性能上可能會差一點,我們還可以使用AtomicInteger
,就可以保證i
原子的++
了。
public class Test {public AtomicInteger i;public void add() {i.getAndIncrement();}
}復制代碼
CAS源碼分析
獲取偏移量valueOffset,
public native long objectFieldOffset(Field var1);通過這個方法可以知道偏移量從jdk底層源碼中獲取。
static {try {valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }
}復制代碼
然后再看看增加的方法
public final int getAndAdd(int delta) {return unsafe.getAndAddInt(this, valueOffset, delta);
}復制代碼
public final int getAndAddInt(Object var1, long var2, int var4) {int var5;do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));return var5;
}復制代碼
var5
獲取的是什么,通過調用unsafe的getIntVolatile(var1, var2)
,這是個native方法,具體實現到JDK源碼里去看了,其實就是獲取var1
中,var2
偏移量處的值。var1
就是AtomicInteger
,var2
就是我們前面提到的valueOffset,這樣我們就從內存里獲取到現在valueOffset處的值了compareAndSwapInt(var1, var2, var5, var5 + var4)
換成compareAndSwapInt(obj, offset, expect, update)
比較清楚,意思就是如果obj
內的value
和expect
相等,就證明沒有其他線程改變過這個變量,那么就更新它為update
,如果這一步的CAS
沒有成功,那就采用自旋的方式繼續進行CAS
操作
private volatile int value;和unsafe.getAndAddInt(this, valueOffset, delta);
可以看出compareAndSwapInt(obj, offset, expect, update)中的obj為AtomicInteger類型,
AtomicInteger的value值為volatile類型,在看do {var5 = this.getIntVolatile(var1, var2);} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));這里是一個do,while循環,如果obj內的value和expect不相等,
var5 = this.getIntVolatile(var1, var2);一直會
執行,即不斷從內存中獲取最新的值,來與obj內的value進行比較直到相等為止。從這個字段可以看出復制代碼
CAS的缺點
- 只能保證對一個變量的原子性操作
- 長時間自旋會給CPU帶來壓力
- ABA問題