日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类

發布時間:2024/1/17 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這篇說說java.util.concurrent.atomic包里的類,總共12個,網上有很多文章解析這幾個類,這里挑些重點說說。

?

這12個類可以分為三組:

1. 普通類型的原子變量

2. 數組類型的原子變量

3. 域更新器

?

普通類型的原子變量的6個,

1. 其中AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference分別對應boolean, int,? long, object完成基本的原子操作

2. AtomicMarkableReference, AtomicStampedReference是AtomicReference的功能增強版本,前者可以把引用跟一個boolean綁定,后者可以把引用和一個int型的版本號綁定來完成時間戳的功能。

?

AtomicBoolean, AtomicInteger, AtomicLong, AtomicReference這幾個類的結構都相似,有幾個特點

1. 底層都是采用sun.misc.Unsafe類來完成實際的CAS操作

2. 使用sun.misc.Unsafe直接操作內存對象來完成類似反射機制的對象屬性存取能力

3. volatile類型的value值保存狀態

4. 原子的get(), set()方法

5. 基本的compareAndSet方法完成CAS操作

6. weakCompareAndSet,弱化版本的CAS操作,這是API設計是預留地差異化接口,但是目前沒有實現,目前和compareAndSet是一樣的功能

7. getAndSet方法是利用CAS操作無鎖地完成讀取并設置的功能

8. lazySet方法優化設置,lazySet的使用看這篇?聊聊高并發(十八)理解AtomicXXX.lazySet方法

?

原子變量在并發編程中是基本的工具,可以用來實現非阻塞的數據結構和構建相關的基礎構件。有幾種基本的用法:

1. 安全的計數器

2. compareAndSet方法可以實現“濾網”的功能,找到第一個成功操作的線程,從而做一些操作,可以看看自旋鎖相關的實現

3. compareAndSet方法可以實現“判斷操作是否成功”的功能,這里會有ABA的問題,可以采用AtomicStampedReference來避免ABA問題

4. getAndSet方法可以實現完全的“設置并返回之前值”的功能

5. AtomicBoolean作為一種二元狀態可以用來作為“開關”,實現找到一個打開開關的線程。比如 if(b.compareAndSet(false, true)){// dosomething}

來看幾個典型的操作

?

?
  • public class AtomicBoolean implements java.io.Serializable {

  • private static final long serialVersionUID = 4654671469794556979L;

  • // setup to use Unsafe.compareAndSwapInt for updates

  • private static final Unsafe unsafe = Unsafe.getUnsafe();

  • private static final long valueOffset;

  • ?
  • // 使用Unsafe直接操作內存的方式設置屬性的值

  • static {

  • try {

  • valueOffset = unsafe.objectFieldOffset

  • (AtomicBoolean.class.getDeclaredField("value"));

  • } catch (Exception ex) { throw new Error(ex); }

  • }

  • // 使用volatile變量來保存狀態

  • private volatile int value;

  • // 使用Unsafe的compareAndSwapXXX方法完成底層的CAS操作

  • public final boolean compareAndSet(boolean expect, boolean update) {

  • ??????? int e = expect ? 1 : 0;

  • ??????? int u = update ? 1 : 0;

  • ??????? return unsafe.compareAndSwapInt(this, valueOffset, e, u);

  • ??? }

  • ?
  • // 無鎖地實現“設置并返回之前值”的功能,無鎖的特點就是輪詢加CAS操作

  • public final boolean getAndSet(boolean newValue) {

  • ??????? for (;;) {

  • ??????????? boolean current = get();

  • ??????????? if (compareAndSet(current, newValue))

  • ??????????????? return current;

  • ??????? }

  • ??? }

  • // 優化volatie變量的寫,再不需要保證可見性的場景下使用lazySet來優化,減少內存屏障

  • public final void lazySet(boolean newValue) {

  • ??????? int v = newValue ? 1 : 0;

  • ??????? unsafe.putOrderedInt(this, valueOffset, v);

  • ??? }


  • AtomicMarkableReference和AtomicStampedReference都是對AtomicReference的增強,內部使用了不可變對象來保存一個二元狀態<Reference, XXX>,當原子設置時,就創建新的對象,并把volaitle引用指向最新的不可變對象。更多內容請查看聊聊高并發(十二)分析java.util.concurrent.atomic.AtomicStampedReference源碼來看如何解決CAS的ABA問題

    ?

    AtomicMarkableReference可以用來標記對象,常用來構建數據結構中表示節點,可以用boolean表示節點是否被刪除

    ?

    ?
  • public class AtomicMarkableReference<V> {

  • ?
  • private static class Pair<T> {

  • final T reference;

  • final boolean mark;

  • private Pair(T reference, boolean mark) {

  • this.reference = reference;

  • this.mark = mark;

  • }

  • static <T> Pair<T> of(T reference, boolean mark) {

  • return new Pair<T>(reference, mark);

  • }

  • }

  • ?
  • private volatile Pair<V> pair;

  • ?
  • public AtomicMarkableReference(V initialRef, boolean initialMark) {

  • pair = Pair.of(initialRef, initialMark);

  • }

  • ?
  • public boolean compareAndSet(V?????? expectedReference,

  • ???????????????????????????????? V?????? newReference,

  • ???????????????????????????????? boolean expectedMark,

  • ???????????????????????????????? boolean newMark) {

  • ??????? Pair<V> current = pair;

  • ??????? return

  • ??????????? expectedReference == current.reference &&

  • ??????????? expectedMark == current.mark &&

  • ??????????? ((newReference == current.reference &&

  • ????????????? newMark == current.mark) ||

  • ???????????? casPair(current, Pair.of(newReference, newMark)));

  • ??? }?


  • ?

    ?

    數組類型的原子變量有3個: AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray, 它們是普通原子變量的數組版本,可以完成對數組中元素的原子操作。

    基本的方法和普通原子類型類似,這里就不重復說了,提一下利用Unsafe讀取數組元素的方法

    1. 利用Unsafe.arrayBaseOffset得到數組的第一個元素的偏移量,因為有對象頭的存在,所以offset不是從0開始

    2. 利用Unsafe.arrayIndexScale得到數組中元素的長度

    3. 利用移位操作代替乘法提高效率。所以先計算shift,比如8字節長度的元素,需要左移3位,相當與2的3次冪,4字節長度的元素左移2位,相當于2的2次冪

    4.?計算數組中元素的實際位置 = index << shift + base,說白了,就是? index * 元素長度? + base

    ?

    ?
  • public class AtomicIntegerArray implements java.io.Serializable {

  • ?
  • private static final Unsafe unsafe = Unsafe.getUnsafe();

  • // 獲得數組第一個元素的偏移量offset,因為有對象頭的存在,所以offset不是從0開始的

  • ?private static final int base = unsafe.arrayBaseOffset(int[].class);

  • // 移位操作的基數,用移位操作代替乘法

  • private static final int shift;

  • private final int[] array;

  • ?
  • static {

  • // 獲取數組元素的長度,對于int[]數組, scale = 4

  • ?int scale = unsafe.arrayIndexScale(int[].class);

  • // 如果長度不是為2的冪,就報錯

  • ?if ((scale & (scale - 1)) != 0)

  • throw new Error("data type scale not a power of two");

  • // 31 - Integer.numberOfLeadingZeros(scale) 相當于求floor(log2(x)),這里為2,如果是Long,就是3

  • // 其實就是用移位操作代替乘法,比如4字節長度,就要左移2位,8字節長度,就要左移3位。左移1位 = 乘 2

  • ?shift = 31 - Integer.numberOfLeadingZeros(scale);

  • }

  • ?
  • private long checkedByteOffset(int i) {

  • if (i < 0 || i >= array.length)

  • throw new IndexOutOfBoundsException("index " + i);

  • ?
  • return byteOffset(i);

  • }

  • // 用移位操作代替乘法,實際上求的是數組的第i個元素的偏移量,方便定位到數組元素的內存地址

  • private static long byteOffset(int i) {

  • return ((long) i << shift) + base;

  • }


  • 最后看看域更新器,有三個: AtomicIntegerFieldUpdate,? AtomicLongFieldUpdate,? AtomicReferenceFieldUpdate

    ?

    域更新器是一種優化手段,它提供了現有volatile域的一種基于反射的視圖,從而能對現有volatile域進行CAS操作,我們知道volatile字段只保證可見性,但是不保證原子性,

    如果要想對volatile字段進行CAS操作,就要用到域更新器。它的好處是可以讓volatile字段具備原子變量的能力,而不需要實際創建這么多的原子變量,畢竟volatile比起原子變量來說還是輕量級的。

    域更新器沒有提供對外的構造函數,它需要利用工廠方法的方式來創建,提供一個newUpdater(xxx)方法來返回一個新建的域更新器對象。

    1. tclass指的是字段所在類的Class類型

    2. vclass指的是字段的Class類型,需要注意的是字段必須是volatile標示的,不然會拋出異常

    3. filedName字段名

    4. 調用者的類型,可以用Reflection.getCallerClass()獲得

    ?

    ?
  • public static <U, W> AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName) {

  • return new AtomicReferenceFieldUpdaterImpl<U,W>(tclass,

  • vclass,

  • fieldName,

  • Reflection.getCallerClass());

  • }

  • ?
  • ? AtomicReferenceFieldUpdaterImpl(Class<T> tclass,

  • ??????????????????????????????????????? Class<V> vclass,

  • ??????????????????????????????????????? String fieldName,

  • ??????????????????????????????????????? Class<?> caller) {

  • ??????????? Field field = null;

  • ??????????? Class fieldClass = null;

  • ??????????? int modifiers = 0;

  • ??????????? try {

  • ??????????????? field = tclass.getDeclaredField(fieldName);

  • ??????????????? modifiers = field.getModifiers();

  • ??????????????? sun.reflect.misc.ReflectUtil.ensureMemberAccess(

  • ??????????????????? caller, tclass, null, modifiers);

  • ??????????????? sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);

  • ??????????????? fieldClass = field.getType();

  • ??????????? } catch (Exception ex) {

  • ??????????????? throw new RuntimeException(ex);

  • ??????????? }

  • ?
  • ??????????? if (vclass != fieldClass)

  • ??????????????? throw new ClassCastException();

  • ?
  • ??????????? if (!Modifier.isVolatile(modifiers))

  • ??????????????? throw new IllegalArgumentException("Must be volatile type");

  • ?
  • ??????????? this.cclass = (Modifier.isProtected(modifiers) &&

  • ?????????????????????????? caller != tclass) ? caller : null;

  • ??????????? this.tclass = tclass;

  • ??????????? if (vclass == Object.class)

  • ??????????????? this.vclass = null;

  • ??????????? else

  • ??????????????? this.vclass = vclass;

  • ??????????? offset = unsafe.objectFieldOffset(field);

  • ??????? }


  • ?

    ?

    AtomicIntegerFieldUpdate這些更新器的接口和原子變量一致,都提供了compareAndSet操作,getAndSet操作等,這里不重復說。舉個例子看看如何使用域更新器

    1. Node類有一個volatile類型的next字段,它沒有使用AtomicReference,使用了更輕量級的volatile

    2. 如果想對volatile類型的next做CAS操作,就要創建域更新器AtomicReferenceFieldUpdater

    ?

    ?
  • private class Node<E>{

  • private final E item;

  • private volatile Node<E> next;

  • ?
  • public Node(E item){

  • this.item = item;

  • }

  • }

  • ?
  • private static AtomicReferenceFieldUpdater<Node, Node> nextUpdate = AtomicReferenceFieldUpdater.newUpdater(Node.class, Node.class, "next");

  • 總結

    以上是生活随笔為你收集整理的聊聊高并发(二十)解析java.util.concurrent各个组件(二) 12个原子变量相关类的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。