Atomic包中原子類的使用,以及內部CAS的實現(xiàn)和原理

上篇文章有說過 多線程環(huán)境下 進行變量屬性 自增操作時會造成線程不安全的情況郑气,也有說到 volatile 關鍵字,最后也不能保證線程安全腰池,因為多線程情況下 他不能保證原子性,不能保證寫操作過程不可以被插隊,最后有提到java.util.current.atomic包中的AtomicInteger類,那么它是如何實現(xiàn)線程安全的呢?尾组,讓我們一探究竟!

image
image.gif

?

atomc包是java專門提供保證原子性的包,里邊提供了基本類型的原子操作類示弓,天生就是保證變量原子性的讳侨。

image
image.gif

?

今天我們就借此先來說一下AtomicInteger,其他類型類的方法 實現(xiàn)方式都一樣奏属。

先對比一下沒有使用前會引發(fā)的狀況:

image
image.gif

?

image
image.gif

?

image
image.gif

?

可以看到沒有達到預想的效果跨跨,并且每次產生的結果都不一樣,這就是上篇

文章所說到的,沒有保證原子性勇婴,在執(zhí)行+1操作時被其他線程插隊忱嘹,導致每次往主內存寫入了相同的值。注:加上volatile也是會產生一樣的結果耕渴!因為volatile不能保證原子性拘悦。

接下來我們使用AtomicInteger來試一下:

image
image.gif

?

image
image.gif

?

可以看到,達到了我們預期的效果橱脸。 那么他到底是是如何實現(xiàn)的呢础米? 我們來一探究竟!

在查看究竟前先講解一個它的一個方法,以及涉及到的知識點,以便于后邊的理解:

先說個點:CAS ==> Compare and Swap ==> 比較且交換

接下來 簡單使用以下AtomicInteger提供的一個方法:

image
image.gif

?

expert:期望值添诉,即 期望改變的值

update:更改值椭盏,即 將期望值更改為什么

這里第一次我期望將初始值1更改為2,操作完成后 我再次期望將1 更改為2吻商,我們看下執(zhí)行結果:

image
image.gif

?

第一次更改 成功 為 true 值變成了2掏颊,第二次執(zhí)行失敗 false 里邊值還是2,第二次沒有被更改過艾帐。這就是所謂的比較交換乌叶。

我們看下這個方法里邊的實現(xiàn):

image
image.gif

?

this , valueOffset 下邊會說是什么意思, expect , update 即是期望值和更改值上邊有說

記住compareAndSwapInt()這個方法柒爸,CAS實現(xiàn)的 關鍵方法

我們先將目光轉到自增方法getAndIncrement方法上看看底層如何實現(xiàn):

image
image.gif

?

解釋下 這個 方法存在的內容:
unsafe:
由于Java方法無法直接訪問底層系統(tǒng)准浴,需要通過本地(native)方法來訪問,Unsafe相當于一個后門捎稚,基于該類可以直接操作特定內存的數(shù)據(jù)乐横。Unsafe類存在于sun.misc包中,其內部方法操作可以像C的指針一樣直接操作內存今野,因為Java中CAS操作的執(zhí)行依賴于Unsafe類的方法葡公。
this:
表示當前AtomicInteger類。
valueOffset:
用一個圖片說明条霜,在AtomicInteger類有聲明:

image
image.gif

?

最后屬性 1 是要增加的數(shù)值 這里是1催什。

讓我們點進去再看看:

image
image.gif

?

上邊我們有看到 compareAndSwapInt 這個方法我們沒有細說,這里說下:

解釋下:
var1: 操作的對象
var2: 內存偏移量地址
var4:要增加的值
var5: 根據(jù)內存偏移量地址獲取到的值 (上邊提到的期望值)
var5 + var4 : 更改為的值

然后這里是個循環(huán)宰睡,先獲取 當前內存偏移量位置 的屬性值作為期望值蒲凶,然后進行修改,如果過程其他線程已經改完了拆内,那么修改返回值為false旋圆,則繼續(xù)循環(huán)重新獲取期望值,再次進行更改麸恍,直到修改成為止才退出循環(huán)灵巧。

1.根據(jù)傳入對象和內存偏移量地址 拿取對應位置最新的值,為期望值

2.進行寫操作,如果過程被其他線程更改孩等,則期望值就會配對不上就會修改失敗艾君,繼續(xù)循環(huán)直到成功。

可能 會有人問 這樣操作進行修改過程中不會被打斷嗎肄方?

對是的冰垄,不會被打斷的,上邊又說Unsafe類中的方法是可以直接訪問計算機內存的权她,可以跟c語言一樣虹茶。

image

image.gif
?
這是在網上找的代碼,內部在向CPU發(fā)送CAS指令時的匯編指令,是一條CPU并發(fā)原語隅要,過程是原子的蝴罪。
CAS并發(fā)語體現(xiàn)在JAVA語言中就是sun.misc.Unsafe類中的各個方法。調用UnSafe類中的CAS方法步清,JVM會幫我們實現(xiàn)出CAS匯編指令要门,這是一種完全依賴于硬件的功能,通過它實現(xiàn)了原子操作廓啊。再次強調欢搜,由于CAS是一種系統(tǒng)原語袋励,原語屬于操作系統(tǒng)用范疇鹏氧,是由若干條指令組成的,用于完成某個功能的一個過程斩郎,并且原語的執(zhí)行必須是連續(xù)的第步,在執(zhí)行過程中不允許被中斷疮装,也就是說CAS是一條CPU的原子指令,不會造成所謂的數(shù)據(jù)不一致問題粘都。所以執(zhí)行過程是不會被打斷的廓推,是線程安全的。

但是 會引發(fā)出來另一個問題切記: ABA問題

什么是ABA:

class ABA{
  int i = 1;
}

假設此時有兩條線程 操作i這個變量
線程1驯杜,2 同時啟動 進行 CAS 操作受啥,他們讀到的期望值都為 1
接下來 兩個線程執(zhí)行以下過程:
線程1: 將 1 更改為 2 做个,然后再將 2 更改為1
線程2:休息 5秒鐘鸽心,將 1 更改為2
線程1 肯定比 線程2 先執(zhí)行完,線程2 執(zhí)行的時候是可以成功將1 更改為2的居暖,但是有一個問題顽频,在他更改的時候他不知道線程1 已經進行了多次更改,將1變?yōu)?又變?yōu)榱?太闺。
就像我桌子上的水被偷喝了糯景,然后喝完又給我接了一杯,而我回來后卻不知道已經被他人喝過了被他人占了個便宜。有種偷天換月的意思蟀淮。

解釋完之后給大家上個理論知識點:
CAS算法實現(xiàn)一個重要前提需要去除內存中某時刻的數(shù)據(jù)并立刻比較并替換最住,那么在這個時間出現(xiàn)時間差類會導致數(shù)據(jù)變化。

這就是很典型的ABA問題怠惶,那么如何解決呢涨缚?
可以加時間戳,版本號都可以解決:
AtomicStampedReference類:

image

image.gif
?
上邊初始化了 值為10 版本號為1的一個 AtomicStampedReference類策治,可以看到同樣再調用compareAndSet方法的時候需要傳4個值:
分別為 期望值脓魏,修改值,期望版本號通惫,修改版本號

有了版本號就可以避免CAS出現(xiàn)ABA的問題茂翔。

Atomic包里邊不只是只有 Integer,Long等基本類型的原子類哦,自定義類同樣可以原子操作:

可以通過AtomicReference類來操作

大家可以試試下邊代碼有時間的話:

image
image.gif

?

總結一下:
為什么明明可以在 自增方法添加一個Synchronized關鍵字就可以解決為什么要通過原子類的CAS來解決履腋。
Synchronized的比較笨重在上方例子珊燎,沒必要殺雞用牛刀,剛好也可以借助上方例子說一下CAS,他在使用時會將它修飾的代碼塊給鎖住遵湖,其他線程不可以訪問俐末,會大大降低并發(fā)。
CAS 則可以大大提升并發(fā)奄侠,線程都可以同時執(zhí)行卓箫,只不過是修改成功與否的問題了。

當然垄潮,這里說CAS也比較多也說一下它的缺點:
CAS雖然可以提升并發(fā)量烹卒,但容易給CPU造成很大的開銷,并且也只能保證一個共享變量的原子性弯洗,對多個共享變量不能同時原子性旅急。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市牡整,隨后出現(xiàn)的幾起案子藐吮,更是在濱河造成了極大的恐慌,老刑警劉巖逃贝,帶你破解...
    沈念sama閱讀 212,454評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谣辞,死亡現(xiàn)場離奇詭異,居然都是意外死亡沐扳,警方通過查閱死者的電腦和手機泥从,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沪摄,“玉大人躯嫉,你說我怎么就攤上這事纱烘。” “怎么了祈餐?”我有些...
    開封第一講書人閱讀 157,921評論 0 348
  • 文/不壞的土叔 我叫張陵擂啥,是天一觀的道長。 經常有香客問我帆阳,道長啤它,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,648評論 1 284
  • 正文 為了忘掉前任舱痘,我火速辦了婚禮变骡,結果婚禮上,老公的妹妹穿的比我還像新娘芭逝。我一直安慰自己塌碌,他們只是感情好,可當我...
    茶點故事閱讀 65,770評論 6 386
  • 文/花漫 我一把揭開白布旬盯。 她就那樣靜靜地躺著台妆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胖翰。 梳的紋絲不亂的頭發(fā)上接剩,一...
    開封第一講書人閱讀 49,950評論 1 291
  • 那天,我揣著相機與錄音萨咳,去河邊找鬼懊缺。 笑死,一個胖子當著我的面吹牛培他,可吹牛的內容都是我干的鹃两。 我是一名探鬼主播,決...
    沈念sama閱讀 39,090評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼舀凛,長吁一口氣:“原來是場噩夢啊……” “哼俊扳!你這毒婦竟也來了?” 一聲冷哼從身側響起猛遍,我...
    開封第一講書人閱讀 37,817評論 0 268
  • 序言:老撾萬榮一對情侶失蹤馋记,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后懊烤,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體梯醒,經...
    沈念sama閱讀 44,275評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,592評論 2 327
  • 正文 我和宋清朗相戀三年奸晴,在試婚紗的時候發(fā)現(xiàn)自己被綠了冤馏。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,724評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡寄啼,死狀恐怖逮光,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情墩划,我是刑警寧澤涕刚,帶...
    沈念sama閱讀 34,409評論 4 333
  • 正文 年R本政府宣布,位于F島的核電站乙帮,受9級特大地震影響杜漠,放射性物質發(fā)生泄漏。R本人自食惡果不足惜察净,卻給世界環(huán)境...
    茶點故事閱讀 40,052評論 3 316
  • 文/蒙蒙 一驾茴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧氢卡,春花似錦锈至、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至筑悴,卻和暖如春们拙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背阁吝。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評論 1 266
  • 我被黑心中介騙來泰國打工砚婆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人突勇。 一個月前我還...
    沈念sama閱讀 46,503評論 2 361
  • 正文 我出身青樓射沟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親与境。 傳聞我的和親對象是個殘疾皇子验夯,可洞房花燭夜當晚...
    茶點故事閱讀 43,627評論 2 350

推薦閱讀更多精彩內容