打開文件管理器Device File Explorer:
版本Android Studio3.2
什么是CAS
它是用在並發場合用來實現線程安全的一種算法,進行不可分割的原子操作。基本思想是:我認為value的值應該是A,如果是的話那我就把它改成B,如果不是A就說明已經被別人修改過了,那么我就不修改了,這就避免了並發情況下多人修改導致出錯。
CAS有三個操作數:內存值V、預期值A、要求改的值B。當且僅當預期值A和內存值V相同時,才將內存值修改為B,否則什么都不做。最后返回現在的V值。
CPU的特殊指令:CAS實際上是要利用CPU的特殊指令,這些指令由CPU保證了他們的原子性,一個指令就可以做好幾件事情,也不會出現線程安全問題。
案例
理解CAS的等價代碼:
public class SimulatedCAS {
private volatile int value;
public synchronized int compareAndSwap(int expectedValue, int newValue) {
int oldValue = value;
if (oldValue == expectedValue) {
value = newValue;
}
return oldValue;
}
}
應用場景
CAS在juc包中的應用是很多的,既能保證安全性,又能提高性能,不需要去獲取互斥同步鎖。CAS的第一個應用就是樂觀鎖,還有並發容器,以及原子類。
以AtomicInteger為例,分析在Java中是如何利用CAS實現原子操作的
關於Java中是如何利用CAS實現原子操作:
-
AtomicInteger加載Unsafe工具,用來直接操作內存數據。
-
所以實際上是通過Unsafe來實現底層操作。
關於Unsafe類:
Unsafe是CAS的核心類。Java無法直接訪問底層操作系統,而是通過本地native方法來訪問。不過盡管如此,JVM還是開啟了一個后門,JDK中的一個類Unsafe,提供了硬件級別的原子操作。
Unsafe代碼中的objectFieldOffset方法獲得的VALUE表示的是變量值在內存中的偏移地址,因為Unsafe就是根據內存偏移地址獲取數據的原值的,這樣我們就能通過Unsafe來實現CAS了。
-
並且還需要volatile修飾value字段,保證可見性。
-
getAndAddInt方法分析
public class AtomicInteger extends Number implements java.io.Serializable {
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
//這個靜態變量會在最初被加載,
//VALUE則是用Unsafe的objectFieldOffset取得的,拿到的是AtomicInteger這個類中value字段的地址。
//而value正是用volatile修飾的
private volatile int value;
//省略。。。
//分析getAndAdd方法
public final int getAndAdd(int delta) {
//調用的是Unsafe的getAndAddInt方法
return U.getAndAddInt(this, VALUE, delta);
}
}
class Unsafe{
//省略。。。
-
總結
Unsafe方法中的compareAndSetInt方法想辦法拿到變量value在內存中的地址。然后通過C++代碼實現原子性比較和替換。
缺點
-
可以添加版本號解決。
-
自旋時間過長