JAVA多線程之AtomicInteger類

前言

atomic翻譯爲原子的,所以顧名思義,這個AtomicInteger的存在就是在Integer的基礎上支持了整型變量的原子性。變量的原子性在我們處理多線程問題的時候都是不可避免需要着重考慮的一個問題,處理不好極可能導致整個邏輯變形,業務處理異常。而且此類問題在發現後再處理會比較耗費時間,影響生產環境的穩定,所以應該在設計時就充分考慮多線程問題。

一、基礎必備-Unsafe

1.1 內存開闢與釋放

大家都知道java不能直接操作內存,屏蔽了指針等操作方法,Unsafe方法裏面提供了allocateMemory,freeMemory等開闢空間和釋放空間的本地方法(本地方法不限制實現語言類型)入口

1.2 元素定位

(1)staticFieldOffset 返回靜態屬性的偏移地址
(2)objectFieldOffset 返回非靜態屬性的偏移地址
(3)staticFieldBase靜態屬性的基地址,即類中第一個靜態屬性的地址,配合偏移地址就能找到靜態屬性的實際地址了
(4)arrayBaseOffset 數組首個元素的偏移地址
(5)arrayIndexScale 數組中元素的相對於首個元素的偏移量,首個元素偏移地址加偏移量就能知道元素的具體地址了

1.3 CAS

在concurrent包中有很多地方都用到了cas操作,最終都是用了Unsafe裏面的cas操作,Unsafe裏面有三個相關的方法入口
(1)compareAndSwapObject(Object var1, long var2, Object var4, Object var5)
(2)compareAndSwapInt(Object var1, long var2, int var4, int var5)
(3)compareAndSwapInt(Object var1, long var2, int var4, int var5)
這三個方法類似,每個方法都有四個入參:操作對象,對象中的元素偏移地址,元素期望值,將要變更的目標值,即該方法對var1中偏移地址爲var2的元素做變更,當它與var4相等時,將它變更成var5,返回true,否則返回false。

二、常用方法分析

進入到類中我們可以看到一個靜態域的初始化過程,有兩個注意點

   static {
        try {
            valueOffset = unsafe.objectFieldOffset
                (AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    private volatile int value;

(1)獲取到了value屬性的偏移地址
(2)value是被volatile修飾的,是線程可見的,volatile修飾的變量在讀寫時存在內存屏障,寫入時會CPU緩存數據刷到內存中,讀取時直接讀取內存數據,同時還能防止指令重排序

2.1 get和set方法

普通的get和set方法,除了volatile使得屬性可見之外和Integer類沒啥區別

2.2 lazySet方法

前面說到volatile修飾的屬性有一系列的操作保證了變量的線程可見性,所以肯定會影響代碼的執行效率,這個方法就是在不需要讓共享變量的修改立刻讓其他線程可見的時候,以設置普通變量的方式來修改共享狀態,可以減少不必要的內存屏障,從而提高程序執行的效率。
PS:基本上很少用到,因爲普通的使用的話肯定優先考慮Integer。

2.3 getAndSet

先獲取內存中的當前變量值,然後不斷的利用cas去更新,直到更新成功,返回更新前的變量值。注意這個更新前的值是在更新前內存中最新的,也就是不會被其他線程有衝突,否則cas不會成功。

  public final int getAndSetInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var4));

        return var5;
    }

2.4 compareAndSet(int expect, int update)

如果expect值和當前內存中的值是一樣的,則將update值更新到內存中

2.5 weakCompareAndSet

和上面的方法一模一樣,換了個名字而已

2.6 getAndIncrement

獲取當前變量的值,然後+1,但是返回的值是+1前的值

  public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
              //獲取當前變量的值
            var5 = this.getIntVolatile(var1, var2);
      // 直到cas成功再退出循環
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

2.7 getAndDecrement

獲取當前變量的值,然後-1,但是返回的值是-1前的值

2.8 getAndAdd(int delta)

獲取當前變量的值,然後加上delta,返回加上delta之前的值

2.9 incrementAndGet

對當前變量執行+1,然後返回+1後的值

2.10 decrementAndGet

對當前變量執行-1,然後返回-1後的值

2.11 addAndGet(int delta)

給當前變量+delta,然後返回+delta的值

其他

其他的幾個方法也大致相同,只不過入參改成了函數而已,熟悉函數式編程的童鞋應該很容易理解


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章