atomicLong源碼分析詳解
- 2019 年 10 月 5 日
- 筆記
版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。
本文鏈接:https://blog.csdn.net/qq_37933685/article/details/80914426
個人部落格:https://suveng.github.io/blog/
atomicLong源碼分析詳解
atomicLong的欄位和實例方法
源碼分析
注意:源碼分析都放在源碼裡面
package java.util.concurrent.atomic; import java.util.function.LongUnaryOperator; import java.util.function.LongBinaryOperator; import sun.misc.Unsafe; /** 一個long值可以用原子更新。 有關原子變數屬性的描述,請參閱java.util.concurrent.atomic包規範。 一個AtomicLong用於諸如原子增量的序列號的應用中,不能用作Long的替代物 。 但是,該類確實擴展了Number ,以允許使用基於數字類的工具和實用程式的統一訪問。 從以下版本開始: 1.5 另請參見: Serialized Form * @since 1.5 * @author Doug Lea */ public class AtomicLong extends Number implements java.io.Serializable { /*serialVersionUID 有什麼作用?該如何使用? serialVersionUID 是實現 Serializable 介面而來的,而 Serializable 則是應用於Java 對象序列化/反序列化。對象的序列化主要有兩種用途: 1. 把對象序列化成位元組碼,保存到指定介質上(如磁碟等) 2. 用於網路傳輸 詳情看下面參考文獻1 這是鏈接https://github.com/giantray/stackoverflow-java-top-qa/blob/master/contents/what-is-a-serialversionuid-and-why-should-i-use-it.md */ private static final long serialVersionUID = 1927816293512124184L; // 設置為使用Unsafe.compareAndSwapInt進行更新 private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; /* 記錄底層JVM是否支援longs的無鎖compareAndSwap。 雖然Unsafe.compareAndSwapLong方法在任何一種情況下都可以工作, 但是應該在Java級別處理一些構造以避免鎖定用戶可見的鎖。 */ static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8(); /* 返回底層JVM是否支援longs的無鎖CompareAndSet。 僅調用一次並快取在VM_SUPPORTS_LONG_CAS中。 */ private static native boolean VMSupportsCS8(); /* 下面這段靜態程式碼塊是用於獲取valueOffset */ static { try { valueOffset = unsafe.objectFieldOffset (AtomicLong.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } // 這個就是AtomicBoolean的關鍵值,value private volatile long value; /** * 用給定的初始值創建一個新的AtomicLong * @param initialValue initialValue - 初始值 */ public AtomicLong(long initialValue) { value = initialValue; } /** * 創建一個新的AtomicLong,初始值為 0 。 */ public AtomicLong() { } /** * 獲取當前值。 * * @return 當前值 */ public final long get() { return value; } /** * 設置為給定值。 * * @param newValue newValue - 新價值 */ public final void set(long newValue) { value = newValue; } /** * 最終設定為給定值。 * * @param newValue newValue - 新價值 * @since 1.6 */ public final void lazySet(long newValue) { unsafe.putOrderedLong(this, valueOffset, newValue); } /** * 這裡拋出一個疑問?到底set()和lazySet()有什麼區別? * 1.首先set()是對volatile變數的一個寫操作, 我們知道volatile的write * 為了保證對其他執行緒的可見性會追加以下兩個Fence(記憶體屏障)如下: * 1.StoreStore 在intel cpu 不存在[寫寫]重排序 * 2.StoreLoad 這個是所有記憶體屏障里最耗性能的記憶體屏障相關參考Doug Lea大大的cookbook (http://g.oswego.edu/dl/jmm/cookbook.html) * Doug Lea大大又說了, lazySet()省去了StoreLoad屏障, 只留下StoreStore * 總結:set()和volatile具有一樣的效果(能夠保證記憶體可見性,能夠避免指令重排序), * 但是使用lazySet不能保證其他執行緒能立刻看到修改後的值(有可能發生指令重排序)。 * 簡單點理解:lazySet比set()具有性能優勢,但是使用場景很有限。 * 在網上沒有找到lazySet和set的性能數據對比, * 而且CPU的速度很快的,應用的瓶頸往往不在CPU, * 而是在IO、網路、資料庫等。對於並發程式要優先保證正確性, * 然後出現性能瓶頸的時候再去解決。因為定位並發導致的問題,往往要比定位性能問題困難很多。 */ /** * 將原子設置為給定值並返回舊值。 * @param newValue newValue - 新的價值 * @return 以前的值 */ public final long getAndSet(long newValue) { return unsafe.getAndSetLong(this, valueOffset, newValue); } /* 如下,實際unsafe.getAndSetLong(this, valueOffset, newValue),還是使用compareAndSwapLong這個本地方法。什麼是本地方法 native method? 簡單地講,一個Native Method就是一個java調用非java程式碼的介面。一個Native Method是這樣一個java的方法:該方法的實現由非java語言實現,比如C。這個特徵並非java所特有,很多其它的程式語言都有這一機制,比如在C++中,你可以用extern 「C」告知C++編譯器去調用一個C的函數。 「A native method is a Java method whose implementation is provided by non-java code.」 在定義一個native method時,並不提供實現體(有些像定義一個java interface),因為其實現體是由非java語言在外面實現的。 public final long getAndSetLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var4)); return var6; } CAS CAS演算法的過程是這樣:它包含3個參數CAS(V,E,N)。V表示要更新的變數,E表示預期值,N表示新值。僅當V 值等於E值時,才會將V的值設為N,如果V值和E值不同,則說明已經有其他執行緒做了更新,則當前執行緒什麼 都不做。最後,CAS返回當前V的真實值。CAS操作是抱著樂觀的態度進行的,它總是認為自己可以成功完成 操作。當多個執行緒同時使用CAS操作一個變數時,只有一個會勝出,並成功更新,其餘均會失敗。失敗的執行緒 不會被掛起,僅是被告知失敗,並且允許再次嘗試,當然也允許失敗的執行緒放棄操作。基於這樣的原理,CAS 操作即時沒有鎖,也可以發現其他執行緒對當前執行緒的干擾,並進行恰當的處理。 */ /** * 如果當前值為 ==為預期值,則將該值原子設置為給定的更新值。 * * @param expect expect - 預期值 * @param update update - 新值 * @return true如果成功 */ public final boolean compareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } /* 這裡是直接調用unsafe.compareAndSwapLong();CAS操作如上面所說 */ /** * 如果當前值為== ,則將原值設置為給定的更新值。 May fail spuriously and does not provide ordering guarantees ,所以只是很少適合替代compareAndSet 。 * * @param expect expect - 預期值 * @param update update - 新值 * @return true如果成功 */ public final boolean weakCompareAndSet(long expect, long update) { return unsafe.compareAndSwapLong(this, valueOffset, expect, update); } /* 延續上面的提問,為什麼可能會失敗?這裡給我跳轉到jdk 的javadoc,我把原文發出來,裡面很詳細,這裡簡單描述一下 這裡是鏈接:https://blog.csdn.net/qq_37933685/article/details/80888972 一個原子類也支援weakCompareAndSet方法,該方法有適用性的限制。在一些平台上,在正常情況下weak版本比compareAndSet更高效, 但是不同的是任何給定的weakCompareAndSet方法的調用都可能會返回一個虛假的失敗( 無任何明顯的原因 )。一個失敗的返回意味著,操作將會重新執行如果需要的話, 重複操作依賴的保證是當變數持有expectedValue的值並且沒有其他的執行緒也嘗試設置這個值將最終操作成功。( 一個虛假的失敗可能是由於記憶體衝突的影響,而和預期值(expectedValue)和當前的值是否相等無關 )。 此外weakCompareAndSet並不會提供排序的保證,即通常需要用於同步控制的排序保證。然而,這個方法可能在修改計數器或者統計,這種修改無關於其他happens-before的程式中非常有用。 當一個執行緒看到一個通過weakCompareAndSet修改的原子變數時,它不被要求看到其他變數的修改,即便該變數的修改在weakCompareAndSet操作之前。 weakCompareAndSet實現了一個變數原子的讀操作和有條件的原子寫操作,但是它不會創建任何happen-before排序, 所以該方法不提供對weakCompareAndSet操作的目標變數以外的變數的在之前或在之後的讀或寫操作的保證。 */ /** * 原子上增加一個當前值。 * @return 以前的值 */ public final long getAndIncrement() { return unsafe.getAndAddLong(this, valueOffset, 1L); } /* public final long getAndAddLong(Object var1, long var2, long var4) { long var6; do { var6 = this.getLongVolatile(var1, var2); } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4)); return var6; } public native long getLongVolatile(Object var1, long var2);//使用本地方法,取的相對這個對象var1的偏移地址為var2的值,簡單來說就是拿到value欄位的值 public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);//CAS操作保證原子性 */ /** * 原子減1當前值。 * * @return 以前的值 */ public final long getAndDecrement() { return unsafe.getAndAddLong(this, valueOffset, -1L); } /* 原理和上面一樣,數值變成-1 */ /** * 將給定的值原子地添加到當前值。 * * @param delta delta - 要添加的值 * @return 以前的值 */ public final long getAndAdd(long delta) { return unsafe.getAndAddLong(this, valueOffset, delta); } /* 原理和上面一樣,數值變成delta */ /** * 原子上增加一個當前值。 * * @return 更新的值 */ public final long incrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L; } /* 原理和上面一樣,數值變成1;返回時舊值+1就是新值,所以返回是新值 */ /** * 原子減1當前值。 * * @return 更新的值 */ public final long decrementAndGet() { return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L; } /* 原理和上面一樣,數值變成-1;返回時舊值-1就是新值,所以返回是新值 */ /** * 將給定的值原子地添加到當前值。 * * @param delta delta - 要添加的值 * @return 更新的值 */ public final long addAndGet(long delta) { return unsafe.getAndAddLong(this, valueOffset, delta) + delta; } /* 原理和上面一樣,數值變成delta;返回時舊值+delta就是新值,所以返回是新值 */ /** * 用應用給定函數的結果原子更新當前值,返回上一個值。 * 該功能應該是無副作用的,因為嘗試的更新由於執行緒之間的爭用而失敗時可能會被重新應用。 * * @param updateFunction updateFunction - 無副作用的功能 * @return 以前的值 * @since 1.8 */ public final long getAndUpdate(LongUnaryOperator updateFunction) { long prev, next; do { prev = get(); next = updateFunction.applyAsLong(prev); } while (!compareAndSet(prev, next)); return prev; } /** * 使用給定函數的結果原子更新當前值,返回更新的值。 該功能應該是無副作用的,因為嘗試的更新由於執行緒之間的爭用而失敗時可能會被重新應用。 * @param updateFunction updateFunction - 無副作用的功能 * @return 更新的值 * @since 1.8 */ public final long updateAndGet(LongUnaryOperator updateFunction) { long prev, next; do { prev = get(); next = updateFunction.applyAsLong(prev); } while (!compareAndSet(prev, next)); return next; } /** * 使用給定函數應用給當前值和給定值的結果原子更新當前值,返回上一個值。 * 該功能應該是無副作用的,因為嘗試的更新由於執行緒之間的爭用而失敗時可能會被重新應用。 該函數應用當前值作為其第一個參數,給定的更新作為第二個參數。 * * @param x x - 更新值 * @param accumulatorFunction accumulatorFunction - 兩個參數的無效副作用 * @return 以前的值 * @since 1.8 */ public final long getAndAccumulate(long x, LongBinaryOperator accumulatorFunction) { long prev, next; do { prev = get(); next = accumulatorFunction.applyAsLong(prev, x); } while (!compareAndSet(prev, next)); return prev; } /** * 使用將給定函數應用於當前值和給定值的結果原子更新當前值,返回更新後的值。 * 該功能應該是無副作用的,因為嘗試的更新由於執行緒之間的爭用而失敗時可能會被重新應用。 該函數應用當前值作為其第一個參數,給定的更新作為第二個參數。 * * @param x x - 更新值 * @param accumulatorFunction accumulatorFunction - 兩個參數的無效副作用 * @return the updated value * @since 1.8 */ public final long accumulateAndGet(long x, LongBinaryOperator accumulatorFunction) { long prev, next; do { prev = get(); next = accumulatorFunction.applyAsLong(prev, x); } while (!compareAndSet(prev, next)); return next; } /* 以上的getAndUpdate(LongUnaryOperator updateFunction);updateAndGet(LongUnaryOperator updateFunction);getAndAccumulate(long x,LongBinaryOperator accumulatorFunction);accumulateAndGet(long x,LongBinaryOperator accumulatorFunction) 四個方法都是用同樣的原理; prev = get();獲取當前的value欄位的值 next = updateFunction.applyAsLong(prev);updateFunction - 無副作用的功能;這是java8 的新特性。lz還不會。 總體來講:這個方法就是去那當前的值,拿到當前值並返回當前值,但是有一點就是保證了返回的時候值還是那個值,沒有被改變,試想就get()拿到了value,但是再多執行緒的轉態可能會被改變,一旦改變了,下面的CAS操作就會失敗; 只有CAS操作成功了就返回值,說明get()之後,value值沒有被其他執行緒改變,這是樂觀鎖的思想 */ /** * Returns the String representation of the current value. * @return the String representation of the current value */ public String toString() { return Long.toString(get()); } /** * Returns the value of this {@code AtomicLong} as an {@code int} * after a narrowing primitive conversion. * @jls 5.1.3 Narrowing Primitive Conversions */ public int intValue() { return (int)get(); } /** * Returns the value of this {@code AtomicLong} as a {@code long}. */ public long longValue() { return get(); } /** * Returns the value of this {@code AtomicLong} as a {@code float} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public float floatValue() { return (float)get(); } /** * Returns the value of this {@code AtomicLong} as a {@code double} * after a widening primitive conversion. * @jls 5.1.2 Widening Primitive Conversions */ public double doubleValue() { return (double)get(); } }
總結:
- lz的Java8新特性掌握不好,看完這個我要去看看Java8的新特性了
- 下一步就對unsafe這個直接操作記憶體的類做一下分析