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