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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

【JDK源码】java.util.concurrent.atomic包常用类详解

發布時間:2024/4/14 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【JDK源码】java.util.concurrent.atomic包常用类详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.


??java.util.concurrent.atomic原子操作類包里面提供了一組原子變量類。其基本的特性就是在多線程環境下,當有多個線程同時執行這些類的實例包含的方法時,具有排他性,即當某個線程進入方法,執行其中的指令時,不會被其他線程打斷,而別的線程就像自旋鎖一樣,一直等到該方法執行完成,才由JVM從等待隊列中選擇一個另一個線程進入,這只是一種邏輯上的理解。實際上是借助硬件的相關指令來實現的,不會阻塞線程(或者說只是在硬件級別上阻塞了)。可以對基本數據、數組中的基本數據、對類中的基本數據進行操作。原子變量類相當于一種泛化的volatile變量,能夠支持原子的和有條件的讀-改-寫操作。

java.util.concurrent.atomic中的類可以分成4組:

標量類:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
數組類:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
更新器類:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
復合變量類:AtomicMarkableReference,AtomicStampedReference

一、標量類:AtomicBoolean,AtomicInteger,AtomicLong,AtomicReference
??AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference這四種基本類型用來處理布爾,整數,長整數,對象四種數據,其內部實現不是簡單的使用synchronized,而是一個更為高效的方式CAS (compare and swap) + volatile和native方法,從而避免了synchronized的高開銷,執行效率大為提升。其實例各自提供對相應類型單個變量的訪問和更新。每個類也為該類型提供適當的實用工具方法。
以AtomicInteger的源碼為例來進行學習:

public class AtomicInteger extends Number implements java.io.Serializable {// ……private static final Unsafe unsafe = Unsafe.getUnsafe();private static final long valueOffset;//……private volatile int value;public AtomicInteger(int initialValue) {value = initialValue;}public AtomicInteger() {}public final int get() {return value;}public final void set(int newValue) {value = newValue;}public final void lazySet(int newValue) {unsafe.putOrderedInt(this, valueOffset, newValue);}public final int getAndSet(int newValue) {for (;;) {int current = get();if (compareAndSet(current, newValue))return current;}}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);} }

1、set()和get()方法可以原子的設定和獲取atomic的數據,類似于volatile,保證數據會在主存中設置或讀取。
2、void set()和void lazySet():set設置為給定值,直接修改原始值;lazySet延時設置變量值,這個等價于set()方法,但是由于字段是volatile類型的,因此次字段的修改會比普通字段(非volatile字段)有稍微的性能延時(盡管可以忽略),所以如果不是想立即讀取設置的新值,允許在“后臺”修改值,那么此方法就很有用。
3、getAndSet()方法
原子的將變量設定為新數據,同時返回先前的舊數據。其本質是get()操作,然后做set()操作。盡管這2個操作都是atomic,但是他們合并在一起的時候,就不是atomic。在Java的源程序的級別上,如果不依賴synchronized的機制來完成這個工作,是不可能的。只有依靠native方法才可以。
4、compareAndSet()和weakCompareAndSet()
這兩個方法都是conditional modifier方法。這2個方法接受2個參數,一個是期望數據(expected),一個是新數據(new);如果atomic里面的數據和期望數據一 致,則將新數據設定給atomic的數據,返回true,表明成功;否則就不設定,并返回false。JDK規范中說:以原子方式讀取和有條件地寫入變量但不 創建任何 happen-before 排序,因此不提供與除 weakCompareAndSet 目標外任何變量以前或后續讀取或寫入操作有關的任何保證。大意就是說調用weakCompareAndSet時并不能保證不存在happen- before的發生(也就是可能存在指令重排序導致此操作失敗)。但是從Java源碼來看,其實此方法并沒有實現JDK規范的要求,最后效果和 compareAndSet是等效的,都調用了unsafe.compareAndSwapInt()完成操作。

??雖然原子的標量類擴展了基本類型的類,但是并沒有擴展基本類型的包裝類,如Integer或Long,事實上它們也不能直接擴展。因為基本類型的包裝類是不可以修改的,而原子變量類是可以修改的。在原子變量類中沒有重新定義hashCode或equals方法,每個實例都是不同的,他們也不宜用做基于散列容器中的鍵值。

二、數組類:AtomicIntegerArray,AtomicLongArray,AtomicReferenceArray
??AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray類進一步擴展了原子操作,對這些類型的數組提供了支持。這些類在為其數組元素提供volatile訪問語義方面也引人注目,這對于普通數組來說是不受支持的。其內部并不是像AtomicInteger一樣維持一個volatile變量,而是全部由native方法實現。數組變量進行volatile沒有意義,因此set/get就需要unsafe來做了,但是多了一個index來指定操作數組中的哪一個元素。

三、更新器類:AtomicLongFieldUpdater,AtomicIntegerFieldUpdater,AtomicReferenceFieldUpdater
?AtomicReferenceFieldUpdater,AtomicIntegerFieldUpdater和AtomicLongFieldUpdater 是基于反射的實用工具,可以提供對關聯字段類型的訪問,可用于獲取任意選定volatile字段上的compareAndSet操作。它們主要用于原子數據結構中,該結構中同一節點的幾個 volatile 字段都獨立受原子更新控制。這些類在如何以及何時使用原子更新方面具有更大的靈活性,但相應的弊端是基于映射的設置較為拙笨、使用不太方便,而且在保證方面也較差。
使用中要注意一下幾點:

(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 來實現的,看下面代碼:

//定義 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; }

四、復合變量類:AtomicMarkableReference,AtomicStampedReference
??AtomicMarkableReference 類將單個布爾值與引用關聯起來。維護帶有標記位的對象引用,可以原子方式更新帶有標記位的引用類型。
??AtomicStampedReference 類將整數值與引用關聯起來。維護帶有整數“標志”的對象引用,可以原子更新帶有版本號的引用類型。該類將整數值與引用關聯起來,可用于原子的更新數據和版本號,可以解決使用CAS進行原子更新時,可能出現的ABA問題。

附錄:java并發編程中的CAS和ABA問題
??CAS(compare and swap)比較和替換是java5+提供的設計并發算法時用到的一種技術。是使用一個期望值和一個變量的當前值進行比較,如果當前值和我們的期望值相等,就使用一個新值替換掉當前值。
CAS適用場景:
我們來看下面一段代碼

class MyLock {private boolean locked = false;public boolean lock() {if(!locked) {locked = true;return true;}return false;} }

??熟悉多線程的同學可以很明顯的看出上面的代碼在多線程環境中會出現問題。
??為了避免在多線程的環境中的錯誤我們可以將locked的檢查和更改放在一個原子代碼塊中執行,因為不存在多線程同時執行原子代碼塊的問題。
我們可以做如下更改:

class MyLock {private boolean locked = false;public synchronized boolean lock() {if(!locked) {locked = true;return true;}return false;} }

以上方法使用了synchronized關鍵字,那么使用CAS可以如何操作呢?

public static classMyLock{private AtomicBoolean locked = new AtomicBoolean(false);public boolean lock(){return locked.compareAndSet(false,true);} }

??此時locked變量不再是boolean類型衛視AtomicBoolean,使用了AtomicBoolean的compareAndSet()方法,用一個期望值和AtomicBoolean實例的值比較,若兩者相等,則使用一個新值替換原來的值。
??但是在使用CAS實現原子操作可能帶來ABA問題.
??我們知道CAS的原理是在比較操作值的時候,檢查值有沒有發生變化,如果沒有發生變化則更新,但是如果一個值原來是A,中間變成B,最后又變成A,那么使用CAS進行檢查時會發現值沒有發生變化,但是實際上是變化了的。
??這個時候該怎么辦呢?這個問題乍一看跟版本沖突的問題類似,所以,我們可以使用添加版本號的方法來解決。
??JDK1.5之后,Atomic包提供的類AtomicStampedReference就解決了這個問題。這個類的compareAndSet方法先檢查當前引用是否等于預期引用,并且檢查當前標志是否等于預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。源碼如下:

public boolean weakCompareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp) {return compareAndSet(expectedReference, newReference,expectedStamp, newStamp); }

總結

以上是生活随笔為你收集整理的【JDK源码】java.util.concurrent.atomic包常用类详解的全部內容,希望文章能夠幫你解決所遇到的問題。

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