jdk1.8對cas操作優化

一、前言

假設現在有多個線程對一個變量不停累加,如果直接對這個變量做 ++ 操作,是有問題的。
多線程對一個data變量時空行修改,是線程不安全的,會導致data值的變化不
遵循預期的值來改變。

二、初初步解決:synchronized

對方法上加synchronized關鍵字,這樣初步的解決“太重了”。
更高效解決方案:Atomic原子類及其底層原理
java併發包下提供了一系列Atomic原子類,比如說AInterger。
他可保證多線程併發安全的情況下,高性能的併發更新一個數值。

三、更高效的方案:Atomic原子類

1、AtomicInteger

private AutomicInteger data = new AutomicInteger(0);
//多個線程併發執行:data.incrementAndGet();

AutomicInteger 累加器,是通過無鎖化CAS(比較並交換)機制保證多線程修改一個數值的安全性。

2、incrementAndGet累加方法

步驟1:每次在修改data變量值時,會先獲取這個值
步驟2:接着執行一個cas操作來修改這值,如果修改成功返回
步驟3:修改失敗,那麼重複步驟1和步驟2,直到成功爲止

public final int addAndGet(int delta) {
   return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
}
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;
}

多個線程併發執行getAndAddInt方法,將data值加1後,再返回累加後最新的值
但這個cas有沒有問題呢?如果有大量線程同時併發修改一個AtomicInterger,可能有很多線程
會不停的自旋,於是java8推出了一個新的類,LongAdder

四、java 8對CAS機制的優化LongAdder

LongAdder:嘗試使用分段CAS以及自動分段遷移的方式來大幅提升多線程高併發執行CAS操作的性能!

1、LongAdder底層原理

transient volatile long base;
transient volatile Cell[] cells;

首先 LongAdder 維護了一個 base 值,這個值和 AtomicLong 中的 value 的作用一樣;除此之外,它還維護了一個 Cell[] cells 數組,初始值爲 null,只有在對 base 執行 CAS 更新失敗時(說明競爭激烈)纔會用到 cells 這個數組

Cell 類很簡單,裏面就放了一個 value 和一個支持 CAS 更新 value 的 cas 方法。它長度是 2 的 n 次冪,也是通過 h & (length - 1) 來獲取數組的下標。

當 n 個線程同時執行 add 方法時,

  • 對於 AtomicLong 來說只會有一個線程會執行成功,剩下的都會失敗進入自旋,最終看起來就像是串行的在執行。
  • 而對於 LongAdder,它會根據線程的 probe 值( Thread 類中的 threadLocalRandomProbe
    字段)求得一個 cells 的數組下標,獲取到 cell 值。n 個線程被分散到不同的 cell 中,在執行 CAS失敗的概率就小得多了。

在這裏插入圖片描述

2、add()方法源碼流程圖:

在這裏插入圖片描述

3、sum()方法:

public long sum() {
   Cell[] as = cells; Cell a;
   long sum = base;
   if (as != null) {
       for (int i = 0; i < as.length; ++i) {
           if ((a = as[i]) != null)
               sum += a.value;
       }
   }
   return sum;
}

sum 方法是返回當前總和,但這個返回值並不是一個原子快照
假如在 sum 的過程中,沒有線程調用 add 方法,這種情況下返回的值是準確的;但在這個過程中,cell[0] 已經被加過了,這時恰好有一個線程調用了 add 方法,對 cell[0] 作了更新,但這個時候 sum 方法已經統計不到了,所以這種值不會被加進來。

4、LongAdder總結

所以它的特點總結起來就是:

  • 在高併發下有更好的性能表現;
  • 但高併發下不能保證 sum 返回值的準確性;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章