AtomicInteger 原子類的做用

AtomicInteger 原子類的做用

  • 多線程操做,Synchronized 性能開銷太大count++不是原子操做。由於count++須要通過讀取-修改-寫入三個步驟。

    • count++不是原子操做。由於count++須要通過讀取-修改-寫入三個步驟。
    • 能夠這樣作:java

      public synchronized void increase() {
          count++;
      }
    • Synchronized鎖是獨佔的,意味着若是有別的線程在執行,當前線程只能是等待!
  • 用CAS操做

    • CAS有3個操做數:算法

      • 內存值V
      • 舊的預期值A
      • 要修改的新值B
    • 當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值(A和內存值V相同時,將內存值V修改成B),而其它線程都失敗,失敗的線程並不會被掛起,而是被告知此次競爭中失敗,並能夠再次嘗試(或者什麼都不作)
    • 咱們能夠發現CAS有兩種狀況:數組

      • 若是內存值V和咱們的預期值A相等,則將內存值修改成B,操做成功!
      • 若是內存值V和咱們的預期值A不相等,通常也有兩種狀況:多線程

        • 重試(自旋)
        • 什麼都不作
    • 理解CAS的核心就是:併發

      CAS是原子性的,雖然你可能看到比較後再修改(compare and swap)以爲會有兩個操做,但終究是原子性的!
  • 原子變量類在java.util.concurrent.atomic包下,整體來看有這麼多個

    • 基本類型:高併發

      • AtomicBoolean:布爾型
      • AtomicInteger:整型
      • AtomicLong:長整型
    • 數組:性能

      • AtomicIntegerArray:數組裏的整型
      • AtomicLongArray:數組裏的長整型
      • AtomicReferenceArray:數組裏的引用類型
    • 引用類型:atom

      • AtomicReference:引用類型
      • AtomicStampedReference:帶有版本號的引用類型
      • AtomicMarkableReference:帶有標記位的引用類型
    • 對象的屬性線程

      • AtomicIntegerFieldUpdater:對象的屬性是整型
      • AtomicLongFieldUpdater:對象的屬性是長整型
      • AtomicReferenceFieldUpdater:對象的屬性是引用類型
    • JDK8新增DoubleAccumulator、LongAccumulator、DoubleAdder、LongAddercode

      • 是對AtomicLong等類的改進。好比LongAccumulator與LongAdder在高併發環境下比AtomicLong更高效。
    • Atomic包裏的類基本都是使用Unsafe實現的包裝類
    • Unsafe裏邊有幾個咱們喜歡的方法(CAS):

      // 第一和第二個參數表明對象的實例以及地址,第三個參數表明指望值,第四個參數表明更新值
      public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
      
      public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
      
      public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
  • 原子變量類使用

    class Count{
        // 共享變量(使用AtomicInteger來替代Synchronized鎖)
        private AtomicInteger count = new AtomicInteger(0);
        public Integer getCount() {
            return count.get();
        }
        public void increase() {
            count.incrementAndGet();
        }
    }
  • 原子類的ABA問題

    • 下面的操做均可以正常執行完的,這樣會發生什麼問題呢??線程C沒法得知線程A和線程B修改過的count值,這樣是有風險的。

      1. 如今我有一個變量count=10,如今有三個線程,分別爲A、B、C
      2. 線程A和線程C同時讀到count變量,因此線程A和線程C的內存值和預期值都爲10
      3. 此時線程A使用CAS將count值修改爲100
      4. 修改完後,就在這時,線程B進來了,讀取獲得count的值爲100(內存值和預期值都是100),將count值修改爲10
      5. 線程C拿到執行權,發現內存值是10,預期值也是10,將count值修改爲11
  • 解決ABA問題

    要解決ABA的問題,咱們可使用JDK給咱們提供的AtomicStampedReference和AtomicMarkableReference類。

    簡單來講就是在給爲這個對象提供了一個版本,而且這個版本若是被修改了,是自動更新的。

    原理大概就是:維護了一個Pair對象,Pair對象存儲咱們的對象引用和一個stamp值。每次CAS比較的是兩個Pair對象

  • LongAdder 性能比 AtomicLong 要好

    • 使用AtomicLong時,在高併發下大量線程會同時去競爭更新同一個原子變量,可是因爲同時只有一個線程的CAS會成功,因此其餘線程會不斷嘗試自旋嘗試CAS操做,這會浪費很多的CPU資源。
    • 而LongAdder能夠歸納成這樣:內部核心數據value分離成一個數組(Cell),每一個線程訪問時,經過哈希等算法映射到其中一個數字進行計數,而最終的計數結果,則爲這個數組的求和累加

      • 簡單來講就是將一個值分散成多個值,在併發的時候就能夠分散壓力,性能有所提升。
相關文章
相關標籤/搜索