java.util.concurrent.atomic原子操作類包

來源:互聯網
上載者:User

標籤:

這個包裡面提供了一組原子變數類。其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的執行個體包含的方法時,具有排他性,即當某個線程進入方法,執行其中的指令時,不會被其他線程打斷,而別的線程就像自旋鎖一樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另一個線程進入,這隻是一種邏輯上的理解。實際上是藉助硬體的相關指令來實現的,不會阻塞線程(或者說只是在硬體層級上阻塞了)。可以對基本資料、數組中的基本資料、對類中的基本資料進行操作。原子變數類相當於一種泛化的volatile變數,能夠支援原子的和有條件的讀-改-寫操作。
    java.util.concurrent.atomic中的類可以分成4組:
標量類(Scalar):AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
數組類:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新器類:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
複合變數類:AtomicMarkableReference,AtomicStampedReference
第一組AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference這四種基本類型用來處理布爾,整數,長整數,對象四種資料,其內部實現不是簡單的使用synchronized,而是一個更為高效的方式CAS (compare and swap) + volatile和native方法,從而避免了synchronized的高開銷,執行效率大為提升。如AtomicInteger的實現片斷為:
Java代碼  收藏代碼

private static final Unsafe unsafe = Unsafe.getUnsafe();  private volatile int value;  public final int get() {          return value;  }  public final void set(int newValue) {          value = newValue;  }  public final boolean compareAndSet(int expect, int update) {      return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  }

建構函式(兩個建構函式)
預設的建構函式:初始化的資料分別是false,0,0,null
帶參建構函式:參數為初始化的資料
set( )和get( )方法:可以原子地設定和擷取atomic的資料。類似於volatile,保證資料會在主存中設定或讀取
void set()和void lazySet():set設定為給定值,直接修改原始值;lazySet延時設定變數值,這個等價於set()方法,但是由於欄位是volatile類型的,因此次欄位的修改會比普通欄位(非volatile欄位)有稍微的效能延時(儘管可以忽略),所以如果不是想立即讀取設定的新值,允許在“後台”修改值,那麼此方法就很有用。
getAndSet( )方法
原子的將變數設定為新資料,同時返回先前的舊資料
其本質是get( )操作,然後做set( )操作。儘管這2個操作都是atomic,但是他們合并在一起的時候,就不是atomic。在Java的來源程式的層級上,如果不依賴synchronized的機制來完成這個工作,是不可能的。只有依靠native方法才可以。
Java代碼  收藏代碼

public final int getAndSet(int newValue) {      for (;;) {          int current = get();          if (compareAndSet(current, newValue))              return current;      }  }

compareAndSet( ) 和weakCompareAndSet( )方法
這 兩個方法都是conditional modifier方法。這2個方法接受2個參數,一個是期望資料(expected),一個是新資料(new);如果atomic裡面的資料和期望資料一 致,則將新資料設定給atomic的資料,返回true,表明成功;否則就不設定,並返回false。JSR規範中說:以原子方式讀取和有條件地寫入變數但不 建立任何 happen-before 排序,因此不提供與除 weakCompareAndSet 目標外任何變數以前或後續讀取或寫入操作有關的任何保證。大意就是說調用weakCompareAndSet時並不能保證不存在happen- before的發生(也就是可能存在指令重排序導致此操作失敗)。但是從Java源碼來看,其實此方法並沒有實現JSR規範的要求,最後效果和 compareAndSet是等效的,都調用了unsafe.compareAndSwapInt()完成操作。
Java代碼  收藏代碼

public final boolean compareAndSet(int expect, int update) {      return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  }  public final boolean weakCompareAndSet(int expect, int update) {      return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  }

 
對於 AtomicInteger、AtomicLong還提供了一些特別的方法。
getAndIncrement( ):以原子方式將當前值加 1,相當於安全執行緒的i++操作。
incrementAndGet( ):以原子方式將當前值加 1, 相當於安全執行緒的++i操作。
getAndDecrement( ):以原子方式將當前值減 1, 相當於安全執行緒的i--操作。
decrementAndGet ( ):以原子方式將當前值減 1,相當於安全執行緒的--i操作。
addAndGet( ): 以原子方式將給定值與當前值相加, 實際上就是等於安全執行緒的i =i+delta操作。
getAndAdd( ):以原子方式將給定值與當前值相加, 相當於安全執行緒的t=i;i+=delta;return t;操作。
以實現一些加法,減法原子操作。(注意 --i、++i不是原子操作,其中包含有3個操作步驟:第一步,讀取i;第二步,加1或減1;第三步:寫回記憶體)
使用AtomicReference建立安全執行緒的堆棧
Java代碼  收藏代碼

package thread;  import java.util.concurrent.atomic.AtomicReference;  public class ConcurrentStack<T> {      private AtomicReference<Node<T>>    stacks  = new AtomicReference<Node<T>>();      public T push(T e) {          Node<T> oldNode, newNode;          for (;;) { // 這裡的處理非常的特別,也是必須如此的。              oldNode = stacks.get();              newNode = new Node<T>(e, oldNode);              if (stacks.compareAndSet(oldNode, newNode)) {                  return e;              }          }      }         public T pop() {          Node<T> oldNode, newNode;          for (;;) {              oldNode = stacks.get();              newNode = oldNode.next;              if (stacks.compareAndSet(oldNode, newNode)) {                  return oldNode.object;              }          }      }         private static final class Node<T> {          private T       object;               private Node<T>   next;                 private Node(T object, Node<T> next) {              this.object = object;              this.next = next;          }      }     }

    雖然原子的標量類擴充了Number類,但並沒有擴充一些基本類型的封裝類,如Integer或Long,事實上他們也不能擴充:基本類型的封裝類是不可以修改的,而原子變數類是可以修改的。在原子變數類中沒有重新定義hashCode或equals方法,每個執行個體都是不同的,他們也不宜用做基於散列容器中的索引值。
      第二組AtomicIntegerArray,AtomicLongArray還有AtomicReferenceArray類進一步擴充了原子操作,對這些類型的數組提供了支援。這些類在為其數組元素提供 volatile 訪問語義方面也引人注目,這對於普通數組來說是不受支援的。
他們內部並不是像AtomicInteger一樣維持一個valatile變數,而是全部由native方法實現,如下
AtomicIntegerArray的實現片斷:
Java代碼  收藏代碼

private static final Unsafe unsafe = Unsafe.getUnsafe();  private static final int base = unsafe.arrayBaseOffset(int[].class);  private static final int scale = unsafe.arrayIndexScale(int[].class);  private final int[] array;  public final int get(int i) {          return unsafe.getIntVolatile(array, rawIndex(i));  }  public final void set(int i, int newValue) {          unsafe.putIntVolatile(array, rawIndex(i), newValue);  }

 
第三組AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater基於反射的工具 + 生產力,可以對指定類的指定 volatile 欄位進行原子更新。API非常簡單,但是也是有一些約束:
(1)欄位必須是volatile類型的
(2)欄位的描述類型(修飾符public/protected/default/private)是與調用者與操作對象欄位的關係一致。也就是說 調用者能夠直接操作對象欄位,那麼就可以反射進行原子操作。但是對於父類的欄位,子類是不能直接操作的,儘管子類可以訪問父類的欄位。
(3)只能是執行個體變數,不能是類變數,也就是說不能加static關鍵字。
(4)只能是可修改變數,不能使final變數,因為final的語義就是不可修改。實際上final的語義和volatile是有衝突的,這兩個關鍵字不能同時存在。
(5)對於AtomicIntegerFieldUpdater 和AtomicLongFieldUpdater 只能修改int/long類型的欄位,不能修改其封裝類型(Integer/Long)。如果要修改封裝類型就需要使用AtomicReferenceFieldUpdater 。
 netty5.0中類ChannelOutboundBuffer統計發送的位元組總數,由於使用volatile變數已經不能滿足,所以使用AtomicIntegerFieldUpdater 來實現的,看下面代碼:
Java代碼  收藏代碼

//定義      private static final AtomicLongFieldUpdater<ChannelOutboundBuffer> TOTAL_PENDING_SIZE_UPDATER =              AtomicLongFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "totalPendingSize");        private volatile long totalPendingSize;        //使用          long oldValue = totalPendingSize;          long newWriteBufferSize = oldValue + size;          while (!TOTAL_PENDING_SIZE_UPDATER.compareAndSet(this, oldValue, newWriteBufferSize)) {              oldValue = totalPendingSize;              newWriteBufferSize = oldValue + size;          }

java.util.concurrent.atomic原子操作類包

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.