JAVA併發包-AtomicInteger

1.簡介

JUC 包提供 了一系列的原子性操作類,這些類都是使用非阻塞算法 CAS 現的 ,相 比使用鎖 現原 性操作這在性能上有很大提高。由於原子性操作類的原理都大致相同。

CAS原理:

在Java發展初期,java語言是不能夠利用硬件提供的這些便利來提升系統的性能的。而隨着java不斷的發展,Java本地方法(JNI)的出現,使得java程序越過JVM直接調用本地方法提供了一種便捷的方式,因而java在併發的手段上也多了起來。而在Doug Lea提供的cucurenct包中,CAS理論是它實現整個java包的基石。

CAS 操作包含三個操作數 —— 內存位置(V)、預期原值(A)和新值(B)。 如果內存位置的值與預期原值相匹配,那麼處理器會自動將該位置值更新爲新值 。否則,處理器不做任何操作。無論哪種情況,它都會在 CAS 指令之前返回該 位置的值。(在 CAS 的一些特殊情況下將僅返回 CAS 是否成功,而不提取當前 值。)CAS 有效地說明了“我認爲位置 V 應該包含值 A;如果包含該值,則將 B 放到這個位置;否則,不要更改該位置,只告訴我這個位置現在的值即可。”

通常將 CAS 用於同步的方式是從地址 V 讀取值 A,執行多步計算來獲得新 值 B,然後使用 CAS 將 V 的值從 A 改爲 B。如果 V 處的值尚未同時更改,則 CAS 操作成功。

類似於 CAS 的指令允許算法執行讀-修改-寫操作,而無需害怕其他線程同時 修改變量,因爲如果其他線程修改變量,那麼 CAS 會檢測它(並失敗),算法 可以對該操作重新計算。

CAS失敗測試:100線程競爭更新,只會有一個成功;

public class AtomicIntegerTest {

    private static AtomicLong atomicLong = new AtomicLong();

    private final static Integer THREAD_NUM = 100;
    
    public static void main(String[] argv) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);

        for (int i = 0; i < THREAD_NUM; ++i) {
            int finalI = i;
            int finalI1 = i;
            new Thread(new Runnable() {
                @Override
                public void run() {
                    boolean compareAndSet = atomicLong.compareAndSet(0, finalI1 + 1);
                    System.out.println(compareAndSet);
                    countDownLatch.countDown();
                }
            }).start();
        }
        countDownLatch.await();

        System.out.println(atomicLong.get());
    }
}

2.測試

    兩個線程統計兩個數組中0的個數,結果爲10,沒有出現其他異常情況;驗證了其線程安全性;

public class AtomicIntegerTest {

    private static AtomicLong atomicLong = new AtomicLong();

    private static Integer[] array1 = new Integer[]{1, 2, 0, 1, 0, 0, 0, 0, 0};

    private static Integer[] array2 = new Integer[]{1, 0, 0, 0, 0, 1, 12, 3, 4};

    private static void incrementValueCountNum(Integer[] array, int targetValue) {
        // System.out.println(Arrays.asList(array1).stream().filter(value->value == 0).count());
        for (int i = 0; i < array.length; ++i) {
            if (array[i].intValue() == targetValue) {
                atomicLong.incrementAndGet();
            }
        }
    }
    public static void main(String[] argv) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);

        new Thread(new Runnable() {
            @Override
            public void run() {
                incrementValueCountNum(array1, 0);
                countDownLatch.countDown();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                incrementValueCountNum(array2, 0);
                countDownLatch.countDown();
            }
        }).start();

        countDownLatch.await();

        System.out.println(atomicLong.get());
    }
}

3.Atmoic-IntegerLong源碼解析

      基本屬性:

public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;
    // 主要依賴unsafe來實現CAS
    // setup to use Unsafe.compareAndSwapLong for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    value字段在AtomicLong內的偏移量
    private static final long valueOffset;

    /**
     * Records whether the underlying JVM supports lockless
     * compareAndSwap for longs. While the Unsafe.compareAndSwapLong
     * method works in either case, some constructions should be
     * handled at Java level to avoid locking user-visible locks.
     */
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

    /**
     * Returns whether underlying JVM supports lockless CompareAndSet
     * for longs. Called only once and cached in VM_SUPPORTS_LONG_CAS.
     */
    private static native boolean VMSupportsCS8();

    static {
        try {
            /// 對valueOffset賦值
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    // 內部維護的值
    private volatile long value;

  public final long incrementAndGet():原子上增加一個當前值;返回增加後的值

public final long incrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, 1L) + 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 final long decrementAndGet():原子減少一個值:返回遞減後的值
 // 同遞增方法一樣,加-1L
 public final long decrementAndGet() {
        return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
 }
public final long addAndGet(long delta):原子加delta,返回增加後的值
同遞增方法
 public final long addAndGet(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
    }
public final boolean compareAndSet(long expect, long update) :CAS更新值
 // 依賴unsafe類
 public final boolean compareAndSet(long expect, long update) {
        return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
    }

getAndAdd,getAndIncreament,getAndDecrment:返回修改之前的數據,但是數據已經修改:

  public final long getAndIncrement() {
        return unsafe.getAndAddLong(this, valueOffset, 1L);
    }

    /**
     * Atomically decrements by one the current value.
     *
     * @return the previous value
     */
    public final long getAndDecrement() {
        return unsafe.getAndAddLong(this, valueOffset, -1L);
    }

    /**
     * Atomically adds the given value to the current value.
     *
     * @param delta the value to add
     * @return the previous value
     */
    public final long getAndAdd(long delta) {
        return unsafe.getAndAddLong(this, valueOffset, delta);
    }

4.總結

  • atmoic包下的原子類,本質都是依靠CAS算法實現的;
  • CAS是用CPU換取上下文開銷的一種方式,因此如果CAS自旋時間過長,存在性能問題,因此jdk提出LongAdder解決這個自旋問題,後續會講;
  •  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章