摘要:原子類的作用多線程操作,性能開銷太大并不是原子操作。每次比較的是兩個對象性能比要好使用時,在高并發下大量線程會同時去競爭更新同一個原子變量,但是由于同時只有一個線程的會成功,所以其他線程會不斷嘗試自旋嘗試操作,這會浪費不少的資源。
AtomicInteger 原子類的作用
多線程操作,Synchronized 性能開銷太大count++并不是原子操作。因為count++需要經過讀取-修改-寫入三個步驟。
count++并不是原子操作。因為count++需要經過讀取-修改-寫入三個步驟。
可以這樣做:
public synchronized void increase() { count++; }
Synchronized鎖是獨占的,意味著如果有別的線程在執行,當前線程只能是等待!
用CAS操作
CAS有3個操作數:
內存值V
舊的預期值A
要修改的新值B
當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能更新變量的值(A和內存值V相同時,將內存值V修改為B),而其它線程都失敗,失敗的線程并不會被掛起,而是被告知這次競爭中失敗,并可以再次嘗試(或者什么都不做)。
我們可以發現CAS有兩種情況:
如果內存值V和我們的預期值A相等,則將內存值修改為B,操作成功!
如果內存值V和我們的預期值A不相等,一般也有兩種情況:
重試(自旋)
什么都不做
理解CAS的核心就是:
CAS是原子性的,雖然你可能看到比較后再修改(compare and swap)覺得會有兩個操作,但終究是原子性的!
原子變量類在java.util.concurrent.atomic包下,總體來看有這么多個
基本類型:
AtomicBoolean:布爾型
AtomicInteger:整型
AtomicLong:長整型
數組:
AtomicIntegerArray:數組里的整型
AtomicLongArray:數組里的長整型
AtomicReferenceArray:數組里的引用類型
引用類型:
AtomicReference:引用類型
AtomicStampedReference:帶有版本號的引用類型
AtomicMarkableReference:帶有標記位的引用類型
對象的屬性
AtomicIntegerFieldUpdater:對象的屬性是整型
AtomicLongFieldUpdater:對象的屬性是長整型
AtomicReferenceFieldUpdater:對象的屬性是引用類型
JDK8新增DoubleAccumulator、LongAccumulator、DoubleAdder、LongAdder
是對AtomicLong等類的改進。比如LongAccumulator與LongAdder在高并發環境下比AtomicLong更高效。
Atomic包里的類基本都是使用Unsafe實現的包裝類
Unsafe里邊有幾個我們喜歡的方法(CAS):
// 第一和第二個參數代表對象的實例以及地址,第三個參數代表期望值,第四個參數代表更新值 public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
原子變量類使用
class Count{ // 共享變量(使用AtomicInteger來替代Synchronized鎖) private AtomicInteger count = new AtomicInteger(0); public Integer getCount() { return count.get(); } public void increase() { count.incrementAndGet(); } }
原子類的ABA問題
下面的操作都可以正常執行完的,這樣會發生什么問題呢??線程C無法得知線程A和線程B修改過的count值,這樣是有風險的。
現在我有一個變量count=10,現在有三個線程,分別為A、B、C
線程A和線程C同時讀到count變量,所以線程A和線程C的內存值和預期值都為10
此時線程A使用CAS將count值修改成100
修改完后,就在這時,線程B進來了,讀取得到count的值為100(內存值和預期值都是100),將count值修改成10
線程C拿到執行權,發現內存值是10,預期值也是10,將count值修改成11
解決ABA問題
要解決ABA的問題,我們可以使用JDK給我們提供的AtomicStampedReference和AtomicMarkableReference類。簡單來說就是在給為這個對象提供了一個版本,并且這個版本如果被修改了,是自動更新的。
原理大概就是:維護了一個Pair對象,Pair對象存儲我們的對象引用和一個stamp值。每次CAS比較的是兩個Pair對象
LongAdder 性能比 AtomicLong 要好
使用AtomicLong時,在高并發下大量線程會同時去競爭更新同一個原子變量,但是由于同時只有一個線程的CAS會成功,所以其他線程會不斷嘗試自旋嘗試CAS操作,這會浪費不少的CPU資源。
而LongAdder可以概括成這樣:內部核心數據value分離成一個數組(Cell),每個線程訪問時,通過哈希等算法映射到其中一個數字進行計數,而最終的計數結果,則為這個數組的求和累加。
簡單來說就是將一個值分散成多個值,在并發的時候就可以分散壓力,性能有所提高。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/73487.html
摘要:提供這些原子類的目的就是為了解決基本類型操作的非原子性導致在多線程并發情況下引發的問題。測試代碼引發的線程問題最終的值為如果是原子操作,那么結果應該就是,反復運行幾次發現結果大部分情況下都不是,這也證明了的非原子性在多線程下產生的問題。 AtomicInteger的原理 java的并發原子包里面提供了很多可以進行原子操作的類,比如: AtomicInteger AtomicBoole...
摘要:即使是在多個線程一起執行的時候,一個操作一旦開始,就不會被其他線程干擾。另外是一個變量,在內存中可見,因此可以保證任何時刻任何線程總能拿到該變量的最新值。 個人覺得這一節掌握基本的使用即可! 本節思維導圖: showImg(https://segmentfault.com/img/remote/1460000016855442?w=1244&h=657); 1 Atomic 原子類介紹...
摘要:今天給大家總結一下,面試中出鏡率很高的幾個多線程面試題,希望對大家學習和面試都能有所幫助。指令重排在單線程環境下不會出先問題,但是在多線程環境下會導致一個線程獲得還沒有初始化的實例。使用可以禁止的指令重排,保證在多線程環境下也能正常運行。 下面最近發的一些并發編程的文章匯總,通過閱讀這些文章大家再看大廠面試中的并發編程問題就沒有那么頭疼了。今天給大家總結一下,面試中出鏡率很高的幾個多線...
摘要:顧名思義,是類型的線程安全原子類,可以在應用程序中以原子的方式更新值。創建對象先來看下對象的創建。也就是說當一個線程修改一個共享變量時,其它線程能立即讀到這個修改的值。 showImg(https://segmentfault.com/img/remote/1460000016012210); 本文首發于一世流云的專欄:https://segmentfault.com/blog... ...
摘要:關鍵字總結有個關鍵字,它們是接下來對其中常用的幾個關鍵字進行概括。而通過關鍵字,并不能解決非原子操作的線程安全性。為了在一個特定對象的一個域上關閉,可以在這個域前加上關鍵字。是語言的關鍵字,用來表示一個域不是該對象串行化的一部分。 java 關鍵字總結 Java有50個關鍵字,它們是: abstract do implements private ...
閱讀 2570·2021-09-06 15:02
閱讀 3200·2021-09-02 10:18
閱讀 2822·2019-08-30 15:44
閱讀 685·2019-08-30 15:43
閱讀 1948·2019-08-30 14:08
閱讀 2758·2019-08-30 13:16
閱讀 1397·2019-08-26 13:52
閱讀 931·2019-08-26 12:21