1什么是CAS
????CAS(Compare And Swap),即比較并交換,是解決多線(xiàn)程并行情況下使用鎖造成性能損耗的一種機(jī)制。
????CAS機(jī)制當(dāng)中使用了3個(gè)基本操作數(shù):內(nèi)存地址V,舊的預(yù)期值A(chǔ),要修改的新值B。
更新一個(gè)變量的時(shí)候,只有當(dāng)變量的預(yù)期值A(chǔ)和內(nèi)存地址V當(dāng)中的實(shí)際值相同時(shí),才會(huì)將內(nèi)存地址V對(duì)應(yīng)的值修改為B。
1.1在內(nèi)存地址V當(dāng)中,存儲(chǔ)著值為10的變量。

1.2此時(shí)線(xiàn)程1想要把變量的值增加1。對(duì)線(xiàn)程1來(lái)說(shuō),舊的預(yù)期值A(chǔ)=10,要修改的新值B=11。

1.3在線(xiàn)程1要提交更新之前,另一個(gè)線(xiàn)程2搶先一步,把內(nèi)存地址V中的變量值率先更新成了11。

1.4線(xiàn)程1開(kāi)始提交更新,首先進(jìn)行A和地址V的實(shí)際值比較(Compare),發(fā)現(xiàn)A不等于V的實(shí)際值,提交失敗。

1.5線(xiàn)程1重新獲取內(nèi)存地址V的當(dāng)前值,并重新計(jì)算想要修改的新值。此時(shí)對(duì)線(xiàn)程1來(lái)說(shuō),A=11,B=12。這個(gè)重新嘗試的過(guò)程被稱(chēng)為自旋。

1.6這一次比較幸運(yùn),沒(méi)有其他線(xiàn)程改變地址V的值。線(xiàn)程1進(jìn)行Compare,發(fā)現(xiàn)A和地址V的實(shí)際值是相等的。

1.7線(xiàn)程1進(jìn)行SWAP,把地址V的值替換為B,也就是12。

從思想上來(lái)說(shuō),Synchronized屬于悲觀(guān)鎖,悲觀(guān)地認(rèn)為程序中的并發(fā)情況嚴(yán)重,所以嚴(yán)防死守。CAS屬于樂(lè)觀(guān)鎖,樂(lè)觀(guān)地認(rèn)為程序中的并發(fā)情況不那么嚴(yán)重,所以讓線(xiàn)程不斷去嘗試更新。
在CAS中,比較和替換是一組原子操作,不會(huì)被外部打斷,且在性能上更占有優(yōu)勢(shì)。
Unsafe提供了三個(gè)方法用于CAS操作
public final native boolean compareAndSwapObject(Object value, long valueOffset, Object expect, Object update);
public final native boolean compareAndSwapInt(Object value, long valueOffset, int expect, int update);
public final native boolean compareAndSwapLong(Object value, long valueOffset, long expect, long update);
解析AtomicInteger
當(dāng)我們使用 AtomicInteger 實(shí)現(xiàn)多線(xiàn)程的加操作時(shí),分析源碼
public class AtomicInteger extends Number implements java.io.Serializable {
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
//帶參數(shù)的構(gòu)造函數(shù)
public AtomicInteger(int initialValue) {
value = initialValue;
}
//缺省構(gòu)造函數(shù)
public AtomicInteger() {
}
public final int get() {
return value;
}
public final void set(int newValue) {
value = newValue;
}
public final void lazySet(int newValue) {
unsafe.putOrderedInt(this, valueOffset, newValue);
}
public final int getAndSet(int newValue) {
return unsafe.getAndSetInt(this, valueOffset, newValue);
}
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final boolean weakCompareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
public final int getAndDecrement() {
return unsafe.getAndAddInt(this, valueOffset, -1);
}
public final int getAndAdd(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta);
}
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
public final int decrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
}
public final int addAndGet(int delta) {
return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
public final int getAndUpdate(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return prev;
}
public final int updateAndGet(IntUnaryOperator updateFunction) {
int prev, next;
do {
prev = get();
next = updateFunction.applyAsInt(prev);
} while (!compareAndSet(prev, next));
return next;
}
public final int getAndAccumulate(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
return prev;
}
public final int accumulateAndGet(int x,
IntBinaryOperator accumulatorFunction) {
int prev, next;
do {
prev = get();
next = accumulatorFunction.applyAsInt(prev, x);
} while (!compareAndSet(prev, next));
return next;
}
public String toString() {
return Integer.toString(get());
}
public int intValue() {
return get();
}
public long longValue() {
return (long)get();
}
public float floatValue() {
return (float)get();
}
public double doubleValue() {
return (double)get();
}
}
解析
Unsafe,是CAS的核心類(lèi),由于Java方法無(wú)法直接訪(fǎng)問(wèn)底層系統(tǒng),需要通過(guò)本地(native)方法來(lái)訪(fǎng)問(wèn),Unsafe相當(dāng)于一個(gè)后門(mén),基于該類(lèi)可以直接操作特定內(nèi)存的數(shù)據(jù)。
變量valueOffset,表示該變量值在內(nèi)存中的偏移地址,因?yàn)閁nsafe就是根據(jù)內(nèi)存偏移地址獲取數(shù)據(jù)的。
變量value用volatile修飾,保證了多線(xiàn)程之間的內(nèi)存可見(jiàn)性. 我們?cè)谑褂胏as更新value的時(shí)候,沒(méi)有用到volatile,但get、set方法獲取value時(shí)用到了volatile的可見(jiàn)性。這樣結(jié)合來(lái)看使得AtomicInteger類(lèi)獲取的是主存中最新的值 且 更新時(shí)cas是原子性操作。
假設(shè)線(xiàn)程A和線(xiàn)程B同時(shí)執(zhí)行g(shù)etAndAdd操作, 因?yàn)楸容^與替換是原子性操作,所以即使在var5獲取到相同的值,也會(huì)是順序的更新。
CAS缺點(diǎn)
- ABA問(wèn)題。
如果在這段期間曾經(jīng)被改成B,然后又改回A,那CAS操作就會(huì)誤認(rèn)為它從來(lái)沒(méi)有被修改過(guò)。針對(duì)這種情況,java并發(fā)包中提供了一個(gè)帶有標(biāo)記的原子引用類(lèi) AtomicStampedReference,它可以通過(guò)控制變量值的版本來(lái)保證CAS的正確性, 該類(lèi)提供一個(gè)引用和計(jì)數(shù)變量。
循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大。自旋CAS如果長(zhǎng)時(shí)間不成功,會(huì)給CPU帶來(lái)非常大的執(zhí)行開(kāi)銷(xiāo)。
只能保證一個(gè)共享變量的原子操作。
當(dāng)對(duì)一個(gè)共享變量執(zhí)行操作時(shí),我們可以使用循環(huán)CAS的方式來(lái)保證原子操作,但是對(duì)多個(gè)共享變量操作時(shí),循環(huán)CAS就無(wú)法保證操作的原子性。
從Java1.5開(kāi)始JDK提供了 AtomicReference 類(lèi)來(lái)保證引用對(duì)象之間的原子性,你可以把多個(gè)變量放在一個(gè)對(duì)象里來(lái)進(jìn)行CAS操作。
AtomicReference與AtomicInteger的不同
AtomicInteger是對(duì)整數(shù)的封裝,底層采用的是compareAndSwapInt實(shí)現(xiàn)CAS,比較的是數(shù)值是否相等。
AtomicReference則對(duì)應(yīng)普通的對(duì)象引用,底層使用的是compareAndSwapObject實(shí)現(xiàn)CAS,比較的是兩個(gè)對(duì)象的地址是否相等。也就是它可以保證你在修改對(duì)象引用時(shí)的線(xiàn)程安全性。也就是它可以保證你在修改對(duì)象引用時(shí)的線(xiàn)程安全性。
小例子
public class Main{
public static void main(String[] args) {
// 創(chuàng)建兩個(gè)Person對(duì)象,它們的id分別是101和102。
Person2 p1 = new Person2(101);
Person2 p2 = new Person2(102);
// 新建AtomicReference對(duì)象,初始化它的值為p1對(duì)象
AtomicReference ar = new AtomicReference(p1);
//更改p1的id.
p1.setId(106);
// 通過(guò)CAS設(shè)置ar。如果ar的值為p1的話(huà),則將其設(shè)置為p2。
ar.compareAndSet(p1, p2);
Person2 p3 = (Person2)ar.get();
System.out.println("p3 is "+p3);
System.out.println("p3.equals(p1)="+p3.equals(p1));
}
}
class Person2 {
volatile long id;
public Person2(long id) {
this.id = id;
}
public String toString() {
return "id:"+id;
}
public void setId(long id){
this.id=id;
}
}
運(yùn)行結(jié)果如下:
p3 is id:102
p3.equals(p1)=false
總結(jié)
修改了p1的id對(duì)compareAndSet()并沒(méi)有影響,因?yàn)樾薷膇d僅僅改變了p1的成員變量,而AtomicReference底層使用的是compareAndSwapObject實(shí)現(xiàn)CAS,比較的是兩個(gè)對(duì)象的地址是否相等。也就是它可以保證你在修改對(duì)象引用時(shí)的線(xiàn)程安全性。上面代碼成功的將原子類(lèi)中的引用從p1變成p2,而且是線(xiàn)程安全的。