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();      }    }

總結:

  1. lz的Java8新特性掌握不好,看完這個我要去看看Java8的新特性了
  2. 下一步就對unsafe這個直接操作記憶體的類做一下分析