JDK併發包溫故知新系列(四)—— CAS原理與JDK8的優化
阿新 • • 發佈:2019-10-04
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
底層通過sun.misc.Unsafe的本地方法compareAndSwapInt實現,這個方法是原子的。
與synchronized的對比
- 樂觀鎖與悲觀鎖的區別
- 效能對比
synchronized是阻塞的,CAS更新是非阻塞的,只是會重試,不會有執行緒上下文切換開銷,對於大部分比較簡單的操作,無論是在低併發還是高併發情況下,這種樂觀非阻塞方式的效能都要遠高於悲觀阻塞式方式。
應用場景
- 用來實現樂觀非阻塞演算法,確保當前執行緒方法體內使用的共享變數不被其他執行緒改變,CAS廣泛運用在非阻塞容器中。
- 用來實現悲觀阻塞式演算法,其用在了顯式鎖的原理實現,如可重入計數中,呼叫lock()方法時將原子變數設為1,返回false的話就一直迴圈不退出,呼叫unlock將原子變數設為0。如果同時多個執行緒呼叫Lock方法那麼必然會導致原子修改不成功,保證了鎖的機制,排他性。
可能存在的問題
- ABA問題,普通的CAS操作並不是原子的,因為有可能另一個執行緒改了值但是又改回了值,那麼樂觀鎖的方式是不能保證原子性的,若業務需要規避這種情況那麼可以使用AtomicStampedReference的
compareAndSet(V expectedReference, V newReference, int expectedStamp, int newStamp)
JDK8的優化
JAVA8新增了LongAdder、DoubleAdder對原子變數進行進一步優化,主要是利用了分段CAS的機制,如果不用LongAdder,用AtomicLong的話,在高併發情況下,會產生一直自旋,導致效率不高。他將一個數分成若干個數,CompareAndSet方法的引數只是比較的這若干個數中的一個數,從而降低了自旋的概率