AtomicLong源碼導讀

AtomicLong是JDK5開始提供的,它的做用是:對長整型進行原子操做。相似的原子類還有:AtomicBoolean、AtomicInteger等,它們的實現原理都是同樣的。html

在原子類出現以前,要想讓一個long類型的數值完成自增操做保持原子性,那麼只能經過加synchronized或者顯式鎖,這種解決方式不只會讓代碼編寫更加複雜,並且效率也不高。java

原子類的出現,提供了另外一種解決思路來保證操做的原子性,那就是:CAS,關於CAS的詳細說明能夠看筆者的另外一篇文章:《關於CAS的一點理解和思考》。緩存

對變量加volatile關鍵字,保證了有序性和可見性,再經過CAS來保證操做的原子性,最終就能保證數據的併發安全。安全

UML 在這裏插入圖片描述 AtomicLong的結構仍是很簡單的,實現了java.io.Serializable接口,表示它能夠被序列化,繼承了java.lang.Number,表明它是一個數值,能夠轉換成其餘數值類型,如int、float。markdown

屬性

AtomicLong的屬性並很少,它依賴於Unsafe類的compareAndSwapLong()方法,只有Unsafe才能夠調用底層的CAS操做。 它記錄了底層JVM是否支持無鎖的方式去更新long類型,double和long這兩個類型比較特殊,佔用64位空間,具體細節後面能夠單獨寫一篇文章記錄下來。 AtomicLong用value表明它的具體數值,被volatile修飾,保證了它的有序性和可見性。併發

// 須要依賴於Unsafe.compareAndSwapLong()來原子的更新value
private static final Unsafe unsafe = Unsafe.getUnsafe();
// value屬性相較於AtomicLong的內存地址偏移量,CAS操做時須要用到
private static final long valueOffset;

/* 記錄底層JVM是否支持無鎖的方式去更新long類型。 由於long和其餘數值有點不同,它佔用8字節,須要佔用兩個32位的空間,存在寫高低位的問題。 */
static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

/* 記錄底層JVM是否支持無鎖的方式去更新long類型,並把它緩存在VM_SUPPORTS_LONG_CAS中。 */
private static native boolean VMSupportsCS8();

// 結果值,volatile保證了它的有序性和可見性。
private volatile long value;
複製代碼

構造函數

AtomicLong提供了兩個構造函數,默認的value值爲0,你也能夠手動指定一個初始值。app

// 手動指定一個初始值
public AtomicLong(long initialValue) {
	value = initialValue;
}

// 使用long的默認值0
public AtomicLong() {
}
複製代碼

核心操做

add

AtomicLong提供了兩個add方法:addAndGetgetAndAddide

addAndGet() 先添加再獲取:函數

/* 以原子的方式加上給定值,再返回 */
public final long addAndGet(long delta) {
	return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}
複製代碼

getAndAdd() 先添加再獲取:oop

/* 以原子的方式加上給定值,返回操做前的結果 */
public final long getAndAdd(long delta) {
	return unsafe.getAndAddLong(this, valueOffset, delta);
}
複製代碼

兩個方法的目的都是將value加上給定的值,無非就是一個返回操做前的值,一個返回操做後的值。

它們調用的方法都是unsafe.getAndAddLong()方法,這纔是核心:

/* 以原子的方式,加上給定值,並返回舊值。 var1:對象實例 var2:value相較於類的內存地址偏移量 var4:加上給定值 */
public final long getAndAddLong(Object var1, long var2, long var4) {
    long var6;
    do {
    	// 從主存中,讀取最新值
        var6 = this.getLongVolatile(var1, var2);
        // CAS的方式將值從var6修改成(var6 + var4),若是失敗就循環重試,直到成功爲止。
    } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
	// 返回舊值
    return var6;
}
複製代碼

實現思路是:從主存中讀取變量最新的值,經過CAS的方式嘗試去修改,若是修改失敗,則說明變量期間已經被其餘線程改過了,當前線程會循環重試,直到成功爲止。

increment

遞增操做,和add同樣,只是add的值爲1。也提供了兩個方法:getAndIncrementincrementAndGet,做用都是值遞增,一個返回舊值,一個返回新值。

/* 以原子的方式,將value遞增,返回舊值。 */
public final long getAndIncrement() {
	// 和add()同樣,遞增就是+1
	return unsafe.getAndAddLong(this, valueOffset, 1L);
}

/* 以原子的方式,將value遞增,返回新值。 */
public final long incrementAndGet() {
	return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}
複製代碼

decrement

遞減操做,依然和add同樣,只是add的值爲-1。也提供了兩個方法:getAndDecrementdecrementAndGet,做用都是值遞減,一個返回舊值,一個返回新值。

/** 以原子的方式,將value遞減,返回舊值。 */
public final long getAndDecrement() {
	// 和add()同樣,遞增就是-1
	return unsafe.getAndAddLong(this, valueOffset, -1L);
}

/** 以原子的方式,將value遞減,返回新值。 */
public final long decrementAndGet() {
	return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}
複製代碼

compareAndSet

比較並設置,以原子的方式,將當前值從expect修改成update,成功返回true,失敗返回false,和CAS一個道理。

/** 以原子的方式,將當前值從expect改成update expect:預期的舊值 update:要修改的新值 */
public final boolean compareAndSet(long expect, long update) {
	return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
複製代碼

仍是依賴於unsafe實現的,unsafe.compareAndSwapLong()就是去調用底層的CAS操做,native方法,使用C編寫,須要調用系統函數。

weakCompareAndSet

不多會用到這個方法,並且在JDK8中,它和compareAndSet代碼如出一轍,沒有任何區別,直到JDK9才被實現。 它的做用是:操做只保留volatile自身的特性,去除happens-before規則帶來的內存語義,即沒法保證沒有別volatile修飾的其餘變量的有序性和可見性。

/** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * <p><a href="package-summary.html#weakCompareAndSet">May fail * spuriously and does not provide ordering guarantees</a>, so is * only rarely an appropriate alternative to {@code compareAndSet}. * * @param expect the expected value * @param update the new value * @return {@code true} if successful */
// JDK8中,和compareAndSet如出一轍
public final boolean weakCompareAndSet(long expect, long update) {
	return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}
複製代碼

總結

原子類的代碼並不複雜,邏輯都很簡單,就是經過volatile+CAS的方式來保證數據的併發安全。 數值的更新幾乎都是依賴於Unsafe類去完成的,CAS操做自己Java代碼不能實現,須要調用本地方法,經過C去調用系統函數。同時CAS自己依賴於現代CPU支持的併發原語,即CPU會保證比較並交換這個過程自己不會被打斷。

相關文章
相關標籤/搜索