在java6以后我們不但接觸到了Lock相關的鎖,也接觸到了很多更加樂觀的原子修改操作,也就是在修改時我們只需要保證它的那個瞬間是安全的即可,經過相應的包裝后可以再處理對象的并發修改,以及并發中的ABA問題,本文講述Atomic系列的類的實現以及使用方法,其中包含:
基本類:AtomicInteger、AtomicLong、AtomicBoolean;
引用類型:AtomicReference、AtomicReference的ABA實例、AtomicStampedRerence、AtomicMarkableReference;
數組類型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
屬性原子修改器(Updater):AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
?
在使用Atomic系列前,我們需要先知道一個東西就是Unsafe類,全名為:sun.misc.Unsafe,這個類包含了大量的對C代碼的操作,包括很多直接內存分配以及原子操作的調用,而它之所以標記為非安全的,是告訴你這個里面大量的方法調用都會存在安全隱患,需要小心使用,否則會導致嚴重的后果,例如在通過unsafe分配內存的時候,如果自己指定某些區域可能會導致一些類似C++一樣的指針越界到其他進程的問題,不過它的具體使用并不是本文的重點,本文重點是Atomic系列的內容大多會基于unsafe類中的以下幾個本地方法來操作:
?
對象的引用進行對比后交換,交換成功返回true,交換失敗返回false,這個交換過程完全是原子的,在CPU上計算完結果后,都會對比內存的結果是否還是原先的值,若不是,則認為不能替換,因為變量是volatile類型所以最終寫入的數據會被其他線程看到,所以一個線程修改成功后,其他線程就發現自己修改失敗了。
參數1:對象所在的類本身的對象(一般這里是對一個對象的屬性做修改,才會出現并發,所以該對象所存在的類也是有一個對象的)
參數2:這個屬性在這個對象里面的相對便宜量位置,其實對比時是對比內存單元,所以需要屬性的起始位置,而引用就是修改引用地址(根據OS、VM位數和參數配置決定寬度一般是4-8個字節),int就是修改相關的4個字節,而long就是修改相關的8個字節。
獲取偏移量也是通過unsafe的一個方法:objectFieldOffset(Fieldfield)來獲取屬性在對象中的偏移量;靜態變量需要通過:staticFieldOffset(Field field)獲取,調用的總方法是:fieldOffset(Fieldfield)
?
參數3:修改的引用的原始值,用于對比原來的引用和要修改的目標是否一致。
參數4:修改的目標值,要將數據修改成什么。
public?final?native?boolean?compareAndSwapObject(Object?paramObject1,?long?paramLong,?Object?paramObject2,?Object?paramObject3);??
??
public?final?native?boolean?compareAndSwapInt(Object?paramObject,?long?paramLong,?int?paramInt1,?int?paramInt2);??
?
#對long的操作,要看VM是否支持對Long的CAS,因為有可能VM本身不支持,若不支持,此時運算會變成Lock方式,不過現在VM都基本是支持的而已。
public?final?native?boolean?compareAndSwapLong(Object?paramObject,?long?paramLong1,?long?paramLong2,?long?paramLong3);??
?
我們不推薦直接使用unsafe來操作原子變量,而是通過java封裝好的一些類來操作原子變量。
?
本文最后要介紹的部分為Updater也就是修改器,它算是Atomic的系列的一個擴展,Atomic系列是為你定義好的一些對象,你可以使用,但是如果是別人已經在使用的對象會原先的代碼需要修改為Atomic系列,此時若全部修改類型到對應的對象相信很麻煩,因為牽涉的代碼會很多,此時java提供一個外部的Updater可以對對象的屬性本身的修改提供類似Atomic的操作,也就是它對這些普通的屬性的操作是并發下安全的,分別由:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceUpdater,這樣操作后,系統會更加靈活,也就是可能那些類的屬性只是在某些情況下需要控制并發,很多時候不需要,但是他們的使用通常有以下幾個限制:
限制1:操作的目標不能是static類型,前面說到unsafe的已經可以猜測到它提取的是非static類型的屬性偏移量,如果是static類型在獲取時如果沒有使用對應的方法是會報錯的,而這個Updater并沒有使用對應的方法。
限制2:操作的目標不能是final類型的,因為final根本沒法修改。
限制3:必須是volatile類型的數據,也就是數據本身是讀一致的。
限制4:屬性必須對當前的Updater所在的區域是可見的,也就是private如果不是當前類肯定是不可見的,protected如果不存在父子關系也是不可見的,default如果不是在同一個package下也是不可見的。
?
實現方式:通過反射找到屬性,對屬性進行操作,但是并不是設置accessable,所以必須是可見的屬性才能操作。
轉載于:https://blog.51cto.com/zhanglida66/1918637