java中的CAS和原子类的实现
什么是CAS
????CAS的全稱為Compare-And-Swap,直譯就是對比交換。是一條CPU的原子指令,其作用是讓CPU先進行比較兩個值是否相等,然后原子地更新某個位置的值,經過調查發現,其實現方式是基于硬件平臺的匯編指令,就是說CAS是靠硬件實現的,JVM只是封裝了匯編調用,那些AtomicInteger類便是使用了這些封裝后的接口。
????簡單解釋:CAS操作需要輸入兩個數值,一個舊值(期望操作前的值)和一個新值,在操作期間先比較下在舊值有沒有發生變化,如果沒有發生變化,才交換成新值,發生了變化則不交換。
????CAS操作是原子性的,所以多線程并發使用CAS更新數據時,可以不使用鎖。JDK中大量使用了CAS來更新數據而防止加鎖(synchronized 重量級鎖)來保持原子更新。
????相信sql大家都熟悉,類似sql中的條件更新一樣:update set id=3 from table where id=2。因為單條sql執行具有原子性,如果有多個線程同時執行此sql語句,只有一條能更新成功。
????如果不使用CAS,在高并發下,多線程同時修改一個變量的值我們需要synchronized加鎖(可能有人說可以用Lock加鎖,Lock底層的AQS也是基于CAS進行獲取鎖的)。
public class Test {private int i=0;public synchronized int add(){return i++;} }????java中為我們提供了AtomicInteger 原子類(底層基于CAS進行更新數據的),不需要加鎖就在多線程并發場景下實現數據的一致性。
public class Test {private AtomicInteger i = new AtomicInteger(0);public int add(){return i.addAndGet(1);} }java.util.concurrent包都中的實現類都是基于volatile和CAS來實現的。尤其java.util.concurrent.atomic包下的原子類。
簡單介紹下volatile特性:
1. 內存可見性(當一個線程修改volatile變量的值時,另一個線程就可以實時看到此變量的更新值)
2. 禁止指令重排(volatile變量之前的變量執行先于volatile變量執行,volatile之后的變量執行在volatile變量之后)
AtomicInteger 源碼解析
public class AtomicInteger extends Number implements java.io.Serializable {private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {//用于獲取value字段相對當前對象的“起始地址”的偏移量valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;//返回當前值public final int get() {return value;}//遞增加detlapublic final int getAndAdd(int delta) {//三個參數,1、當前的實例 2、value實例變量的偏移量 3、當前value要加上的數(value+delta)。return unsafe.getAndAddInt(this, valueOffset, delta);}//遞增加1public final int incrementAndGet() {return unsafe.getAndAddInt(this, valueOffset, 1) + 1;} ... }我們可以看到 AtomicInteger 底層用的是volatile的變量和CAS來進行更改數據的。
volatile保證線程的可見性,多線程并發時,一個線程修改數據,可以保證其它線程立馬看到修改后的值
CAS 保證數據更新的原子性。
Unsafe源碼解析
下面分析下Unsafe 類中的實現。代碼反編譯出來的。
public final int getAndAddInt(Object paramObject, long paramLong, int paramInt){int i;doi = getIntVolatile(paramObject, paramLong);while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt));return i;}public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2){long l;dol = getLongVolatile(paramObject, paramLong1);while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2));return l;}public final int getAndSetInt(Object paramObject, long paramLong, int paramInt){int i;doi = getIntVolatile(paramObject, paramLong);while (!compareAndSwapInt(paramObject, paramLong, i, paramInt));return i;}public final long getAndSetLong(Object paramObject, long paramLong1, long paramLong2){long l;dol = getLongVolatile(paramObject, paramLong1);while (!compareAndSwapLong(paramObject, paramLong1, l, paramLong2));return l;}public final Object getAndSetObject(Object paramObject1, long paramLong, Object paramObject2){Object localObject;dolocalObject = getObjectVolatile(paramObject1, paramLong);while (!compareAndSwapObject(paramObject1, paramLong, localObject, paramObject2));return localObject;}從源碼中發現,內部使用自旋的方式進行CAS更新(while循環進行CAS更新,如果更新失敗,則循環再次重試)。
又從Unsafe類中發現,原子操作其實只支持下面三個方法。
public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3);public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3);我們發現Unsafe只提供了3種CAS方法:compareAndSwapObject、compareAndSwapInt和compareAndSwapLong。都是native方法。
AtomicBoolean 源碼解析
public class AtomicBoolean implements java.io.Serializable {private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset = unsafe.objectFieldOffset(AtomicBoolean.class.getDeclaredField("value"));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;public AtomicBoolean(boolean initialValue) {value = initialValue ? 1 : 0;}public final boolean compareAndSet(boolean expect, boolean update) {int e = expect ? 1 : 0;int u = update ? 1 : 0;return unsafe.compareAndSwapInt(this, valueOffset, e, u);}... }從AtomicBoolean源碼,發現他底層也是使用volatile類型的int 變量,跟AtomicInteger 實現方式一樣,只不過是把Boolean轉換成 0和1進行操作。
所以原子更新char、float和double變量也可以轉換成int 或long來實現CAS的操作。
CAS缺點
從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等于預期引用,并且當前標志是否等于預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。
本人簡書blog地址:http://www.jianshu.com/u/1f0067e24ff8????
點擊這里快速進入簡書
總結
以上是生活随笔為你收集整理的java中的CAS和原子类的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 常量池之字符串常量池String.int
- 下一篇: JVM 类加载机制深入浅出