CAS(Compare and swap)是設(shè)計并發(fā)算法時用到的一種技術(shù)棍厌。相比傳統(tǒng)的鎖和同步技術(shù),資源競爭較少的情況下竖席,CAS可以節(jié)約更多的資源耘纱。
基本思路
CAS的基本思路其實不算特別復(fù)雜,用下面這個偽代碼來說明CAS的核心:
CAS(V,E,N)
在上述函數(shù)中:
- V: 要被更新的變量內(nèi)存地址
- E: 舊的值
- N: 更新之后的值
CAS()
函數(shù)會返回布爾值毕荐,僅有V的值等于E的時候束析,V的值才會被設(shè)置成N。如果V和E的值不一致憎亚,說明這個變量已經(jīng)被別的線程修改员寇,那么CAS()
函數(shù)就會返回false
弄慰。
可以用以下的思路來實現(xiàn)無鎖地正確修改一個值:
do{
備份舊數(shù)據(jù);
基于舊數(shù)據(jù)構(gòu)造新數(shù)據(jù)蝶锋;
}while(!CAS( 內(nèi)存地址陆爽,舊數(shù)據(jù),新數(shù)據(jù) ))
AtomicInteger的例子
在jdk的atomic
包下扳缕,擁有大量使用CAS輔助完成的類慌闭。
在此之前,先介紹下Unsafe
這個類躯舔,這個類在sun.misc
包下的驴剔,這個類里面封裝了一些不安全的操作(主要是Oracle不希望大家使用的方法),這些大多數(shù)是一些native
的方法粥庄,其中跟CAS密切相關(guān)的一個方法compareAndSwapInt()
方法:
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
以上方法中丧失,第一個參數(shù)var1
是給定的要操作對象,var2
是字段到對象頭部的偏移量(方便快速定位到字段)惜互,var4
表示舊的值布讹,var5
表示希望設(shè)置的值。
接下來看到Unsafe
中另外一個方法利用這個CAS函數(shù)來無鎖地添加值:
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
// 獲取當(dāng)前對象的值
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
以上這段代碼的思路和偽代碼中CAS是一個套路载佳,在當(dāng)前線程正確修改了值之后,將原值返回臀栈。
這個時候回到AtomicInteger
蔫慧,先看下它的兩個參數(shù):
// 這里面存著包裝類的值
private volatile int value;
// 這里保存了value屬性在AtomicInteger對象的偏移量,方便定位屬性的位置
private static final long valueOffset;
接著看它最經(jīng)典的兩個自增方法就非常簡單了权薯,它們利用Unsafe
的getAndAddInt()
就可以輕松地完成無鎖的正確自增:
/**
* Atomically increments by one the current value.
*
* @return the updated value
*/
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
/**
* Atomically increments by one the current value.
*
* @return the previous value
*/
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}