Conmpare And Swap(比較并且交換),樂觀鎖的一種實(shí)現(xiàn)方式
1.5引入CAS即Java原子類
java.util.concurrent.atomic
jdk所提供的原子類可以大致分為四種類型:
- 原子更新基本數(shù)據(jù)類型,AtomicLong,AtomicInteger,AtomicBoolean
- 原子更新數(shù)組類型,AtomicLongArray,AtomicIntegerArray
- 原子更新對(duì)象引用,AtomicReference
- 原子更新字段(基于反射),AtomicIntegerFieldUpdater
以AtomicInteger的compareAndSet為例
/CAS操作有三個(gè)操作數(shù):內(nèi)存值,預(yù)期值,更新值/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
//valueOffset是value在內(nèi)存中的偏移量
private static final long valueOffset;
private volatile int value;
Unsafe是Java中一個(gè)底層類,包含了很多基礎(chǔ)的操作,比如數(shù)組操作、對(duì)象操作、內(nèi)存操作、CAS操作、線程(park)操作、柵欄(Fence)操作,JUC包、一些三方框架都使用Unsafe類來保證并發(fā)安全。
特點(diǎn):1.循環(huán)(自旋)開銷大;2.只能保證一個(gè)變量的原子性操作
3.ABA問題
指在CAS操作時(shí),其他線程將變量值A(chǔ)改為了B,但是又被改回了A,等到本線程使用期望值A(chǔ)與當(dāng)前變量進(jìn)行比較時(shí),發(fā)現(xiàn)變量A沒有變,于是CAS就將A值進(jìn)行了交換操作,但是實(shí)際上該值已經(jīng)被其他線程改變過,這與樂觀鎖的設(shè)計(jì)思想不符合。
一般的解決思路:添加版本號(hào),每次變量更新的時(shí)候把變量的版本號(hào)加1,那么A-B-A就會(huì)變成A1-B2-A3,只要變量被某一線程修改過,改變量對(duì)應(yīng)的版本號(hào)就會(huì)發(fā)生遞增變化,從而解決了ABA問題。
JDK的解決方法:在java.util.concurrent.atomic包中提供了AtomicStampedReference和AtomicMarkableReference
public class AtomicStampedReference<V> {
//省略源碼其余部分
/* 構(gòu)造函數(shù)
* @param initialRef the initial reference 初始引用
* @param initialStamp the initial stamp 相當(dāng)于初始版本號(hào)
*/
public AtomicStampedReference(V initialRef, int initialStamp) {
pair = Pair.of(initialRef, initialStamp);
}
/*核心*/
public boolean compareAndSet(V expectedReference,
V newReference,
int expectedStamp,
int newStamp) {
Pair<V> current = pair;
return
//當(dāng)reference和stamp都相等時(shí),才允許CAS操作
expectedReference == current.reference &&
expectedStamp == current.stamp &&
((newReference == current.reference &&
newStamp == current.stamp) ||
casPair(current, Pair.of(newReference, newStamp)));
}
}
AtomicMarkableReference是AtomicStampedReference的簡(jiǎn)化版,不關(guān)心修改過幾次,僅僅關(guān)心是否修改過。因此變量mark是boolean類型,僅記錄值是否有過修改。
public AtomicMarkableReference(V initialRef, boolean initialMark) { pair = Pair.of(initialRef, initialMark);}
public boolean compareAndSet(V expectedReference,
V newReference,
boolean expectedMark,
boolean newMark) {
Pair<V> current = pair;
return
expectedReference == current.reference &&
expectedMark == current.mark &&
((newReference == current.reference &&
newMark == current.mark) ||
casPair(current, Pair.of(newReference, newMark)));
}