java併發包之原子類Automic

1.概述

  • Java從JDK1.5開始提供了java.util.concurrent.atomic包,方便程序員在多線程環境下,無鎖的進行原子操作。原子變量的底層使用了處理器提供的原子指令,但是不同的CPU架構可能提供的原子指令不一樣,也有可能需要某種形式的內部鎖,所以該方法不能絕對保證線程不被阻塞。
  • 原子類其內部實現不是簡單的使用synchronized,而是一個更爲高效的方式CAS (compare and swap) + volatile和native方法(同步的工作更多的交給了硬件),從而避免了synchronized的高開銷,執行效率大爲提升
  • 雖然基於CAS的線程安全機制很好很高效,但要說的是,並非所有線程安全都可以用這樣的方法來實現,這隻適合一些粒度比較小,型如計數器這樣的需求用起來纔有效,否則也不會有鎖的存在了
  • 在Atomic包裏一共有12個類,四種原子更新方式,分別是原子更新基本類型,原子更新數組,原子更新引用和原子更新字段。Atomic包裏的類基本都是使用Unsafe實現的包裝類。
    這裏寫圖片描述

2.代碼詳解

現在我們來分析一下Atomic包中AtomicInteger的源代碼,其它類的源代碼在原理上都比較類似。

  • 有參構造函數:從構造函數函數可以看出,數值存放在成員變量value中;
  • 成員變量value聲明爲volatile類型,說明了多線程下的可見性,即任何一個線程的修改,在其它線程中都會被立刻看到
public AtomicInteger(int initialValue) { 
  value = initialValue;
}

  • compareAndSet方法(value的值通過內部this和valueOffset傳遞)
  • 這個方法就是最核心的CAS操作
  • 接收2個參數,一個是期望數據(expected),一個是新數據(new);如果atomic裏面的數據和期望數據一致,則將新數據設定給atomic的數據,返回true,表明成功;否則就不設定,並返回false。
public final boolean compareAndSet(int expect, int update) {
 return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

  • getAndSet方法,在該方法中調用了compareAndSet方法
  • 本質是get( )操作,然後做set( )操作。儘管這2個操作都是atomic,但是他們合併在一起的時候,就不是atomic
  • 如果在執行if(compareAndSet(current,newValue)之前其它線程更改了value的值,那麼導致value的值必定和current的值不同,compareAndSet執行失敗,只能重新獲取value的值,然後繼續比較,直到成功。
public final int getAndSet(int newValue) {
    for (;;) {
      int current = get();
      if (compareAndSet(current, newValue))
        return current;
    }
}

  • i++的實現
  • 一般來說自增和自減都不是原子操作,其中包含有3個操作步驟:第一步,讀取i;第二步,加1或減1;第三步,寫回內存
  • 爲了實現原子性,必須在for循環中比較值value是否被修改
public final int getAndIncrement() {
    for (;;) {
      int current = get();
      int next = current + 1;
      if (compareAndSet(current, next))
        return current;
    }
}

  • ++i的實現
public final int incrementAndGet() {
    for (;;) {
      int current = get();
      int next = current + 1;
      if (compareAndSet(current, next))
        return next;
    }
}

  • 實例:使用AutomicInteger實現隨機數字生成器
puclic class AutomicRandom{
    private AutomicInteger seed;
    AutomicRandom(int seed){
        this.seed = new AutomicInteger(seed);
    }
    public int nextInt(){
        while(true){
            int s = seed.get();
            int nextSeed = calculateNext(s);
            if(seed.compareAndSet(s,nextSeed)){ 
                 //對比新舊數據是否一致,一致則設置
                //新的數字成功設置到原子類對象裏面
                int remainder = s % n;
                return remainder > 0 ? remainder : remainder + n;
            }
        }
    }
}

3. 總結

  • 在中低程度的競爭下,原子類提供更高的伸縮性;在高強度的競爭下,鎖能更好的幫助我們避免競爭。(來自《併發編程實戰》)
  • 所以,我們要視情況而定,若資源競爭規模不大,控制粒度較小,使用原子類比使用鎖更好,能提高效率與性能
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章