AtomicInteger源碼分析詳解

  • 2019 年 10 月 5 日
  • 筆記

版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接和本聲明。

本文鏈接:https://blog.csdn.net/qq_37933685/article/details/80871395

無鎖類源碼分析之AtomicInteger

在並發基礎中寫到並發級別非阻塞和阻塞,那非阻塞也分三個級別,無障礙,無鎖,無等待。

並發基礎鏈接

環境:jdk1.8 IntelliJ IDEA2017

無鎖類的原理詳解

1.1.CAS CAS演算法的過程是這樣:它包含3個參數CAS(V,E,N)。V表示要更新的變數,E表示預期值,N表示新值。僅當V 值等於E值時,才會將V的值設為N,如果V值和E值不同,則說明已經有其他執行緒做了更新,則當前執行緒什麼 都不做。最後,CAS返回當前V的真實值。CAS操作是抱著樂觀的態度進行的,它總是認為自己可以成功完成 操作。當多個執行緒同時使用CAS操作一個變數時,只有一個會勝出,並成功更新,其餘均會失敗。失敗的執行緒 不會被掛起,僅是被告知失敗,並且允許再次嘗試,當然也允許失敗的執行緒放棄操作。基於這樣的原理,CAS 操作即時沒有鎖,也可以發現其他執行緒對當前執行緒的干擾,並進行恰當的處理。

無鎖類之AtomicInteger

  1. AtomicInteger 概述
    • public class AtomicInteger extends Number implements Serializable 一個int可能原子更新的值。 一個AtomicInteger用於諸如原子增量計數器的應用程式中,不能用作Integer的替代品 。 但是,這個類確實擴展了Number以允許通過處理基於數字類的工具和實用程式的統一訪問。 AtomicInteger的類方法如下所示:

注意:這裡我貼上AtomicInteger的源碼,解析都寫在下方源碼裡面

package java.util.concurrent.atomic;  import java.util.function.IntUnaryOperator;  import java.util.function.IntBinaryOperator;  import sun.misc.Unsafe;      public class AtomicInteger extends Number implements java.io.Serializable {      private static final long serialVersionUID = 6214790243416807050L;        // 設置為使用Unsafe.compareAndSwapInt進行更新      private static final Unsafe unsafe = Unsafe.getUnsafe();      private static final long valueOffset;//value值偏移量              /**       * Unsafe類是用來在任意記憶體地址位置處讀寫數據,可見,對於普通用戶來說,使用起來還是比較危險的。       * public native long objectFieldOffset(Field var1);方法用於獲取某個欄位相對Java對象的「起始地址」的偏移量,方法返回值和參數如下       * AtomicInteger.class.getDeclaredField("value")是拿到atomicInteger的value欄位的field對象       * valueoffset是拿到value的相對於AtomicInteger對象的地址偏移量       *       */      static {          try {              valueOffset = unsafe.objectFieldOffset                  (AtomicInteger.class.getDeclaredField("value"));          } catch (Exception ex) { throw new Error(ex); }      }            /*      *這裡的int value就是atomicInteger 的值下面的操作都是圍繞value展開      */      private volatile int value;        /**構造方法       * 使用給定的初始值創建新的AtomicInteger。即value = initialValue;       *       * @param 傳入初始值       */      public AtomicInteger(int initialValue) {          value = initialValue;      }              /**       * 創建一個初始值為0的新AtomicInteger實例。即value=0;       */      public AtomicInteger() {      }                /**       * 獲取當前值。       *       * @return 當前的價值       */      public final int get() {          return value;      }              /**       * 設置為給定值。       *       * @param newValue 新值       */      public final void set(int newValue) {          value = newValue;      }              /**       * 最終設置為給定值。       *       * @param newValue the new value       * @since 1.6       */      public final void lazySet(int newValue) {          unsafe.putOrderedInt(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 新值       * @return 之前的值       */      public final int getAndSet(int newValue) {          return unsafe.getAndSetInt(this, valueOffset, newValue);      }      /**       * unsafe.getAndSetInt(this, valueOffset, newValue);的方法程式碼如下       * public final int getAndSetInt(Object var1, long var2, int var4) {          int var5;          do {              var5 = this.getIntVolatile(var1, var2);//getIntVolatile方法獲取對象中offset偏移地址對應的整型field的值,支援volatile load語義。          } while(!this.compareAndSwapInt(var1, var2, var5, var4));//var1 是傳進來的對象,long var2 是欄位偏移值,int var5 是舊值,var4 是新值,var1和var2相當於當前記憶體的值當記憶體的值與舊值相等時,函數才能返回true,cas演算法實現這個無鎖操作            return var5;//var5 是傳進來的舊值,所以上面的函數說設置定值並返回舊值      }       */              /**       * 以原子方式將值設置為給定的更新值       * 如果當前值@code ==的預期值。       *       * @param expect 預期值       * @param update 新值       * @return 成功返回true 返回false表明實際值不等於預期值,設置失敗       */      public final boolean compareAndSet(int expect, int update) {          return unsafe.compareAndSwapInt(this, valueOffset, expect, update);      }        /**       * 以原子方式將值設置為給定的更新值       * 如果當前值@code ==的預期值。       *       * <p><a href="package-summary.html#weakCompareAndSet"可能會失敗       * 虛假並且不提供訂購保證</a>, 也是       * 很少是一個合適的替代品 {@code compareAndSet}.       * 該方法可能可能虛假的失敗並且不會提供一個排序的保證,所以它在極少的情況下用於代替compareAndSet方法。       *       * @param expect 期望值       * @param update 新值       * @return {@code true} if successful       */      public final boolean weakCompareAndSet(int expect, int update) {          return unsafe.compareAndSwapInt(this, valueOffset, expect, update);      }      /**       * 一個巨大的問題:weakCompareAndSet 和 compareAndSet 之間有什麼區別?       * 看源碼的doc是這樣子解釋的: 該方法可能可能虛假的失敗並且不會提供一個排序的保證,所以它在極少的情況下用於代替compareAndSet方法。       * 從jdk 8 的官方文檔的java.util.concurrent.atomic上找到這麼二段話       * (原文)The atomic classes also support method weakCompareAndSet, which has limited applicability.       * On some platforms, the weak version may be more efficient than compareAndSet in the normal case, but differs in that any given invocation of the weakCompareAndSet method may return false spuriously (that is, for no apparent reason).       * A false return means only that the operation may be retried if desired, relying on the guarantee that repeated invocation when the variable holds expectedValue and no other thread is also attempting to set the variable will eventually succeed.       *  (Such spurious failures may for example be due to memory contention effects that are unrelated to whether the expected and current values are equal.) Additionally weakCompareAndSet does not provide ordering guarantees that are usually needed for synchronization control.       *  However, the method may be useful for updating counters and statistics when such updates are unrelated to the other happens-before orderings of a program. When a thread sees an update to an atomic variable caused by a weakCompareAndSet, it does not necessarily see updates to any other variables that occurred before the weakCompareAndSet.       *  This may be acceptable when, for example, updating performance statistics, but rarely otherwise.       *  (譯文)一個原子類也支援weakCompareAndSet方法,該方法有適用性的限制。在一些平台上,在正常情況下weak版本比compareAndSet更高效,但是不同的是任何給定的weakCompareAndSet方法的調用都可能會返回一個虛假的失敗( 無任何明顯的原因 )。一個失敗的返回意味著,操作將會重新執行如果需要的話,重複操作依賴的保證是當變數持有expectedValue的值並且沒有其他的執行緒也嘗試設置這個值將最終操作成功。( 一個虛假的失敗可能是由於記憶體衝突的影響,而和預期值(expectedValue)和當前的值是否相等無關 )。此外weakCompareAndSet並不會提供排序的保證,即通常需要用於同步控制的排序保證。然而,這個方法可能在修改計數器或者統計,這種修改無關於其他happens-before的程式中非常有用。當一個執行緒看到一個通過weakCompareAndSet修改的原子變數時,它不被要求看到其他變數的修改,即便該變數的修改在weakCompareAndSet操作之前。       *  (原文)weakCompareAndSet atomically reads and conditionally writes a variable       *  but does not create any happens-before orderings,       *  so provides no guarantees with respect to previous or subsequent reads       *  and writes of any variables other than the target of the weakCompareAndSet.       *  (譯文)weakCompareAndSet實現了一個變數原子的讀操作和有條件的原子寫操作       * ,但是它不會創建任何happen-before排序       * ,所以該方法不提供對weakCompareAndSet操作的目標變數以外的變數的在之前或在之後的讀或寫操作的保證。       *       */              /**       * 原子上增加一個當前值。       *       * @return 之前的值       */      public final int getAndIncrement() {          return unsafe.getAndAddInt(this, valueOffset, 1);      }      /**getAndAddInt函數的方法體,傳進來的var4是1,每調用一次增加1.compareAndSwapInt前面解釋過了       *public final int getAndAddInt(Object var1, long var2, int var4) {          int var5;          do {              var5 = this.getIntVolatile(var1, var2);          } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));            return var5;      }         */        /**       * 原子地將當前值減1。       *       * @return 之前的值       */      public final int getAndDecrement() {          return unsafe.getAndAddInt(this, valueOffset, -1);      }      //和上面一樣,傳進去的var4是-1,所以每次調用減1        /**       * 原子上將給定值添加到當前值。       *       * @param delta 添加值       * @return 舊值       */      public final int getAndAdd(int delta) {          return unsafe.getAndAddInt(this, valueOffset, delta);      }      //和上面一樣,但是這裡傳進去的var4是delta而已              /**       * 原子上當前值加1。       *       * @return 新值       */      public final int incrementAndGet() {          return unsafe.getAndAddInt(this, valueOffset, 1) + 1;      }      //前面說getAndAddInt返回的是之前的值,調用這個函數把這個值+1,返回+1前的值之後+1,就等於現在的值          /**       * 原子地將當前值減1。       *       * @return 新值       */      public final int decrementAndGet() {          return unsafe.getAndAddInt(this, valueOffset, -1) - 1;      }      //還是一樣。換成-1.變成減而已            /**       * 原子上將給定值添加到當前值。       *       * @param delta 添加值       * @return 新值       */      public final int addAndGet(int delta) {          return unsafe.getAndAddInt(this, valueOffset, delta) + delta;      }      //還是一樣。換成delta值              /**       * 用應用給定函數的結果原子地更新當前值,返回以前的值。 該函數應該是無副作用的,因為當嘗試的更新由於執行緒之間的爭用而失敗時,它可能會被重新應用。       *       * @param updateFunction 無副作用的功能       * @return 舊值       * @since 1.8新特性誒       */      public final int getAndUpdate(IntUnaryOperator updateFunction) {          int prev, next;          do {              prev = get();              next = updateFunction.applyAsInt(prev);          } while (!compareAndSet(prev, next));          return prev;      }      /**       * updateFunction.applyAsInt(prev);中使用了函數式編程       * 通過函數式編程,可以靈活的實現更加複雜的原子操作。       * # 清單5 IntUnaryOperator介面              int applyAsInt(int operand);                default IntUnaryOperator compose(IntUnaryOperator before) {                  Objects.requireNonNull(before);                  return (int v) -> applyAsInt(before.applyAsInt(v));              }                default IntUnaryOperator andThen(IntUnaryOperator after) {                  Objects.requireNonNull(after);                  return (int t) -> after.applyAsInt(applyAsInt(t));              }          該介面定義了一個待實現方法和兩個默認方法,通過compose和andThen即可實現多個IntUnaryOperator的組合調用。       */              /**       *用應用給定函數的結果原子地更新當前值,返回以前的值。 該函數應該是無副作用的,因為當嘗試的更新由於執行緒之間的爭用而失敗時,它可能會被重新應用。       *       * @param updateFunction a side-effect-free function       * @return the updated value       * @since 1.8       */      public final int updateAndGet(IntUnaryOperator updateFunction) {          int prev, next;          do {              prev = get();              next = updateFunction.applyAsInt(prev);          } while (!compareAndSet(prev, next));          return next;      }      /**       * 老問題,updateandget和getandupdate的區別       * 看return GetAndUpdate返回舊值,updateAndGet返回新值,       */              /**       * Atomically updates the current value with the results of       * applying the given function to the current and given values,       * returning the previous value. The function should be       * side-effect-free, since it may be re-applied when attempted       * updates fail due to contention among threads.  The function       * is applied with the current value as its first argument,       * and the given update as the second argument.       *       * @param x the update value       * @param accumulatorFunction a side-effect-free function of two arguments       * @return the previous value       * @since 1.8       */      public final int getAndAccumulate(int x,                                        IntBinaryOperator accumulatorFunction) {          int prev, next;          do {              prev = get();              next = accumulatorFunction.applyAsInt(prev, x);          } while (!compareAndSet(prev, next));          return prev;      }        /**       * 將給定函數應用於當前值和給定值的結果以原始方式更新當前值,並返回以前的值。 該函數應該是無副作用的,因為當嘗試的更新由於執行緒之間的爭用而失敗時,它可能會被重新應用。 該函數以當前值作為第一個參數,給定更新作為第二個參數應用。       *       * @param x the update value       * @param accumulatorFunction a side-effect-free function of two arguments       * @return the updated value       * @since 1.8       */      public final int accumulateAndGet(int x,                                        IntBinaryOperator accumulatorFunction) {          int prev, next;          do {              prev = get();              next = accumulatorFunction.applyAsInt(prev, x);          } while (!compareAndSet(prev, next));          return next;      }            //下面的這些沒什麼好說的get() set()之類的        /**       * Returns the String representation of the current value.       * @return the String representation of the current value       */      public String toString() {          return Integer.toString(get());      }        /**       * Returns the value of this {@code AtomicInteger} as an {@code int}.       */      public int intValue() {          return get();      }        /**       * Returns the value of this {@code AtomicInteger} as a {@code long}       * after a widening primitive conversion.       * @jls 5.1.2 Widening Primitive Conversions       */      public long longValue() {          return (long)get();      }        /**       * Returns the value of this {@code AtomicInteger} 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 AtomicInteger} as a {@code double}       * after a widening primitive conversion.       * @jls 5.1.2 Widening Primitive Conversions       */      public double doubleValue() {          return (double)get();      }    }

java native 方法怎麼實現

什麼是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語言在外面實現的。

native標識符意義

標識符native可以與所有其它的java標識符連用,但是abstract除外。這是合理的,因為native暗示這些方法是有實現體的,只不過這些實現體是非java的,但是abstract卻顯然的指明這些方法無實現體。native與其它java標識符連用時,其意義同非Native Method並無差別,比如native static表明這個方法可以在不產生類的實例時直接調用,這非常方便,比如當你想用一個native method去調用一個C的類庫時。上面的第三個方法用到了native synchronized,JVM在進入這個方法的實現體之前會執行同步鎖機制(就像java的多執行緒。)

native的特性

一個native method方法可以返回任何java類型,包括非基本類型,而且同樣可以進行異常控制。

當一個native method接收到一些非基本類型時如Object或一個整型數組時,這個方法可以訪問這非些基本型的內部,但是這將使這個native方法依賴於你所訪問的java類的實現。

有一點要牢牢記住:我們可以在一個native method的本地實現中訪問所有的java特性,但是這要依賴於你所訪問的java特性的實現,而且這樣做遠遠不如在java語言中使用那些特性方便和容易。

native method的存在並不會對其他類調用這些本地方法產生任何影響,實際上調用這些方法的其他類甚至不知道它所調用的是一個本地方法。

JVM將控制調用本地方法的所有細節

如果一個含有本地方法的類被繼承,子類會繼承這個本地方法並且可以用java語言重寫這個方法(這個似乎看起來有些奇怪),同樣的如果一個本地方法被fianl標識,它被繼承後不能被重寫。

本地方法非常有用,因為它有效地擴充了jvm.事實上,我們所寫的java程式碼已經用到了本地方法,在sun的java的並發(多執行緒)的機制實現中,許多與作業系統的接觸點都用到了本地方法,這使得java程式能夠超越java運行時的界限。有了本地方法,java程式可以做任何應用層次的任務。

為什麼要使用Native Method

1.與java環境外交互:

​ 有時java應用需要與java外面的環境交互。這是本地方法存在的主要原因,你可以想想java需要與一些底層系統如作業系統或某些硬體交換資訊時的情況。本地方法正是這樣一種交流機制:它為我們提供了一個非常簡潔的介面,而且我們無需去了解java應用之外的繁瑣的細節。

2.與作業系統交互:

JVM支援著java語言本身和運行時庫,它是java程式賴以生存的平台,它由一個解釋器(解釋位元組碼)和一些連接到本地程式碼的庫組成。然而不管怎 樣,它畢竟不是一個完整的系統,它經常依賴於一些底層(underneath在下面的)系統的支援。這些底層系統常常是強大的作業系統。通過使用本地方法,我們得以用java實現了jre的與底層系統的交互,甚至JVM的一些部分就是用C寫的,還有,如果我們要使用一些java語言本身沒有提供封裝的作業系統的特性時,我們也需要使用本地方法。

3.Sun』s Java

Sun的解釋器是用C實現的,這使得它能像一些普通的C一樣與外部交互。jre大部分是用java實現的,它也通過一些本地方法與外界交互。例如:類java.lang.Thread 的 setPriority()方法是用java實現的,但是它實現調用的是該類里的本地方法setPriority0()。這個本地方法是用C實現的,並被植入JVM內部,在Windows 95的平台上,這個本地方法最終將調用Win32 SetPriority() API。這是一個本地方法的具體實現由JVM直接提供,更多的情況是本地方法由外部的動態鏈接庫(external dynamic link library)提供,然後被JVM調用。

JVM怎樣使Native Method跑起來:

我們知道,當一個類第一次被使用到時,這個類的位元組碼會被載入到記憶體,並且只會回載一次。在這個被載入的位元組碼的入口維持著一個該類所有方法描述符的list,這些方法描述符包含這樣一些資訊:方法程式碼存於何處,它有哪些參數,方法的描述符(public之類)等等。 如果一個方法描述符內有native,這個描述符塊將有一個指向該方法的實現的指針。這些實現在一些DLL文件內,但是它們會被作業系統載入到java程式的地址空間。當一個帶有本地方法的類被載入時,其相關的DLL並未被載入,因此指向方法實現的指針並不會被設置。當本地方法被調用之前,這些DLL才會被載入,這是通過調用java.system.loadLibrary()實現的。

參考文獻:

https://blog.csdn.net/wike163/article/details/6635321

http://cmsblogs.com/?p=2235

http://www.blogjava.net/mstar/archive/2013/04/24/398351.html

http://ifeve.com/juc-atomic-class-lazyset-que/

https://www.jianshu.com/p/33998c5c0247

https://blog.csdn.net/qfycc92/article/details/46489553

對 volatile、compareAndSet、weakCompareAndSet 的一些思考

Tags: