Atomic原子類面經(jīng)總結(jié)

首先來看看Atomic原子類的結(jié)構(gòu)

Atomic原子類的結(jié)構(gòu)

一、Automic概念

automic在java中為JUC(java.util.concurrent)并發(fā)包的一個(gè)子包。顧名思義,automic包中包含許多原子類。

1、什么是原子類?

原子是不可分割的最小單位,故原子類可認(rèn)為是其操作是不可分割的。

2、原子類的作用?

提供一種簡單、性能高效、線程安全地更新一個(gè)變量的方式。


二、分析Automic底層邏輯(以AtomicInteger自增為例)

通過前面volatile的講解,大家應(yīng)該也對(duì)為什么要有原子類操作有了一定的了解,那我們都知道,i++自增操作不是原子性的,也就是說在高并發(fā)的情況下也許會(huì)發(fā)生重復(fù)覆蓋,這顯然不是我們想要的,那么Atomic原子類就是來解決這個(gè)問題的,下面我們可以看來來AtomicInteger自增源碼:

AtomicInteger自增源碼

可以看到AtomicInteger的自增操作返回unsafe.getAndAddInt()方法。

這里大家可能有疑問了,什么是unsafe呢?

對(duì)于JVM有過了解的小伙伴可能知道JNI,也就是java本地庫接口,Unsafe(sun.misc.Unsafe)是屬于JNI的類,Unsafe里面的native方法直接操作內(nèi)存,getUnfate()僅提供高級(jí)的Bootstrap類加載器使用,簡而言之就是直接操作CPU;

接下來查看unsafe.getAndAddInt()方法源碼

unsafe.getAndAddInt()方法源碼

可以發(fā)現(xiàn)我們?cè)赿o...while的循環(huán)條件調(diào)用了compareAndSwapInt(var1, var2, var5, var5 + var4)方法,即AtomicInteger中的CAS算法。

PS:此處對(duì)于CAS(CompareAndSwap)以及之前的volatile不熟悉的同學(xué)可以看看我的另外一篇文章,那篇文章更適合在看JUC之前了解為什么要學(xué)習(xí)JUC,思路會(huì)更清晰。


三、CAS

CAS是計(jì)算機(jī)硬件對(duì)并發(fā)操作共享數(shù)據(jù)的支持,CAS包含三個(gè)操作數(shù):

1、內(nèi)存值V(var1,var2)

2、預(yù)估值A(chǔ)(var 5)

3、更新值B(var5 + var4)

只有當(dāng)V==A時(shí),才會(huì)把B的值賦給V,即V=B,否則不做任何操作。

不用CAS時(shí),線程修改數(shù)據(jù),有從主內(nèi)存取得數(shù)據(jù)、修改數(shù)據(jù)、回寫數(shù)據(jù)這三步,在回寫數(shù)據(jù)這一步,可能主內(nèi)存已經(jīng)被別的線程回寫過了,就會(huì)發(fā)生回寫覆蓋。為了解決這個(gè),CAS在回寫的時(shí)候會(huì)將主內(nèi)存與自己工作內(nèi)存之前取得的值比較一下,就可以判斷是否已經(jīng)被別人修改過了,如果沒有,我就可以修改,不過別人修改過了,我再去一次,在最新的值上面再修改。

為什么用CAS不用Synchronized呢?

鎖之后,最耗時(shí)的就是線程的上下文交換,也就是線程被掛起和被喚醒的過程,在這個(gè)過程中,操作系統(tǒng)會(huì)在核態(tài)和用戶態(tài)之間切換,要保存運(yùn)行環(huán)境和恢復(fù)運(yùn)行環(huán)境,這些都是很費(fèi)事的,在JAVA1.6之后優(yōu)化的自旋鎖就是為了避免上下文交換。

PS:關(guān)于操作系統(tǒng)核態(tài)和用戶態(tài)是什么以及怎樣切換面試也問的挺多的,感興趣的同學(xué)可以看看我操作系統(tǒng)相關(guān)的文章和面經(jīng)。

通過上面的分析可以看到,do,while循環(huán)也避免了線程的上下文交換,性能會(huì)高很多。


CAS的缺點(diǎn)

1、雖然CAS沒有掛起線程、增加并發(fā)性,但是當(dāng)很多個(gè)線程進(jìn)行自旋,就會(huì)白白耗費(fèi)CPU資源,這種并發(fā),就只是讓很多線程在那循環(huán)等待。

2、只能保證一個(gè)共享變量的原子操作。CAS要有一個(gè)compare,一個(gè)變量就好對(duì)比,如果是段代碼,一個(gè)類呢?synchronized就能鎖這樣的,當(dāng)時(shí)還有AtomicReference等類有相應(yīng)的操作。

3、ABA問題

先有兩個(gè)線程,其中一個(gè)運(yùn)行的比較快,將主內(nèi)存中的變量從A改成了B,再從B改成了A,這個(gè)時(shí)候另外一個(gè)線程調(diào)用compareAndSwap方法時(shí),會(huì)發(fā)現(xiàn)期望值于實(shí)際值是相同的,它并不知道中間已經(jīng)被修改過了,它就會(huì)修改掉這個(gè)值。

當(dāng)我們的實(shí)際問題不能容忍這個(gè)被修改的過程,這個(gè)問題就很嚴(yán)重了,比如會(huì)員卡的問題,用戶消費(fèi)20元,緊接著再充值20元,如果積分統(tǒng)計(jì)線程是根據(jù)余額的減少來統(tǒng)計(jì)積分的,那就嚴(yán)重了,積分統(tǒng)計(jì)線程并不知道用戶已經(jīng)消費(fèi)過了,積分沒統(tǒng)計(jì)上,這是一個(gè)大漏洞。

4、如何解決ABA問題?

在加個(gè)變量(時(shí)間戳)來表示這個(gè)變量是否被修改過了,這樣下來,再執(zhí)行 compareAndSet()方法的時(shí)候,不僅僅比較這個(gè)變量是否相同,還要比較時(shí)間戳是否相等。

AtomicStampReference類則實(shí)現(xiàn)了這個(gè)功能

AtomicStampReference構(gòu)造函數(shù)和compareAndSet()方法

AtomicStampReference構(gòu)造函數(shù)和compareAndSet()方法

這里我們以AtomicInteger為例講解了Atomic原子類的作用,通過分析AtomicInteger自增源碼我們可以知道Atomic原子類的底層是CAS,這里對(duì)于CAS也進(jìn)行了較為詳細(xì)的講解,CAS本身也是面試中經(jīng)常會(huì)遇到的問題,不熟悉的同學(xué)需要進(jìn)行詳細(xì)的了解。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容