自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

為什么程序員招聘都要5年經(jīng)驗起?因為他們懂Java 8底層優(yōu)化

開發(fā) 前端
這篇文章給大家聊一下Java并發(fā)包下的CAS相關(guān)的原子操作,以及Java 8如何改進和優(yōu)化CAS操作的性能。

一、前情回顧

上篇文章給大家聊了一下volatile的原理,具體參見:??入坑兩個月自研非外包創(chuàng)業(yè)公司,居然讓我搞懂了volatile??

這篇文章給大家聊一下java并發(fā)包下的CAS相關(guān)的原子操作,以及Java 8如何改進和優(yōu)化CAS操作的性能。

因為Atomic系列的原子類,無論在并發(fā)編程、JDK源碼、還是各種開源項目中,都經(jīng)常用到。而且在Java并發(fā)面試中,這一塊也屬于比較高頻的考點,所以還是值得給大家聊一聊。

二、場景引入,問題凸現(xiàn)

好,我們正式開始!假設(shè)多個線程需要對一個變量不停的累加1,比如說下面這段代碼:

實際上,上面那段代碼是不ok的,因為多個線程直接這樣并發(fā)的對一個data變量進行修改,是線程不安全性的行為,會導(dǎo)致data值的變化不遵照預(yù)期的值來改變。

舉個例子,比如說20個線程分別對data執(zhí)行一次data++操作,我們以為最后data的值會變成20,其實不是。

最后可能data的值是18,或者是19,都有可能,因為多線程并發(fā)操作下,就是會有這種安全問題,導(dǎo)致數(shù)據(jù)結(jié)果不準(zhǔn)確。

至于為什么會不準(zhǔn)確?那不在本文討論的范圍里,因為這個一般只要是學(xué)過java的同學(xué),肯定都了解過多線程并發(fā)問題。

三、初步的解決方案:synchronized

所以,對于上面的代碼,一般我們會改造一下,讓他通過加鎖的方式變成線程安全的:

這個時候,代碼就是線程安全的了,因為我們加了synchronized,也就是讓每個線程要進入increment()方法之前先得嘗試加鎖,同一時間只有一個線程能加鎖,其他線程需要等待鎖。

通過這樣處理,就可以保證換個data每次都會累加1,不會出現(xiàn)數(shù)據(jù)錯亂的問題。

老規(guī)矩!我們來看看下面的圖,感受一下synchronized加鎖下的效果和氛圍,相當(dāng)于N個線程一個一個的排隊在更新那個數(shù)值。

但是,如此簡單的data++操作,都要加一個重磅的synchronized鎖來解決多線程并發(fā)問題,就有點殺雞用牛刀,大材小用了。

雖然隨著Java版本更新,也對synchronized做了很多優(yōu)化,但是處理這種簡單的累加操作,仍然顯得“太重了”。人家synchronized是可以解決更加復(fù)雜的并發(fā)編程場景和問題的。

而且,在這個場景下,你要是用synchronized,不就相當(dāng)于讓各個線程串行化了么?一個接一個的排隊,加鎖,處理數(shù)據(jù),釋放鎖,下一個再進來。

四、更高效的方案:Atomic原子類及其底層原理

對于這種簡單的data++類的操作,其實我們完全可以換一種做法,java并發(fā)包下面提供了一系列的Atomic原子類,比如說AtomicInteger。

他可以保證多線程并發(fā)安全的情況下,高性能的并發(fā)更新一個數(shù)值。我們來看下面的代碼:

大家看上面的代碼,是不是很簡單!多個線程可以并發(fā)的執(zhí)行AtomicInteger的incrementAndGet()方法,意思就是給我把data的值累加1,接著返回累加后最新的值。

這個代碼里,就沒有看到加鎖和釋放鎖這一說了吧!

實際上,Atomic原子類底層用的不是傳統(tǒng)意義的鎖機制,而是無鎖化的CAS機制,通過CAS機制保證多線程修改一個數(shù)值的安全性

那什么是CAS呢?他的全稱是:Compare and Set,也就是先比較再設(shè)置的意思。

話不多說,先上圖!

我們來看上面的圖,假如說有3個線程并發(fā)的要修改一個AtomicInteger的值,他們底層的機制如下:

首先,每個線程都會先獲取當(dāng)前的值,接著走一個原子的CAS操作,原子的意思就是這個CAS操作一定是自己完整執(zhí)行完的,不會被別人打斷。

然后CAS操作里,會比較一下說,唉!大兄弟!現(xiàn)在你的值是不是剛才我獲取到的那個值???

如果是的話,bingo!說明沒人改過這個值,那你給我設(shè)置成累加1之后的一個值好了!

同理,如果有人在執(zhí)行CAS的時候,發(fā)現(xiàn)自己之前獲取的值跟當(dāng)前的值不一樣,會導(dǎo)致CAS失敗,失敗之后,進入一個無限循環(huán),再次獲取值,接著執(zhí)行CAS操作!

好!現(xiàn)在我們對照著上面的圖,來看一下這整個過程:

  • 首先第一步,我們假設(shè)線程一咔嚓一下過來了,然后對AtomicInteger執(zhí)行incrementAndGet()操作,他底層就會先獲取AtomicInteger當(dāng)前的值,這個值就是0。
  • 此時沒有別的線程跟他搶!他也不管那么多,直接執(zhí)行原子的CAS操作,問問人家說:兄弟,你現(xiàn)在值還是0嗎?
  • 如果是,說明沒人修改過?。√昧?,給我累加1,設(shè)置為1。于是AtomicInteger的值變?yōu)?!
  • 接著線程2和線程3同時跑了過來,因為底層不是基于鎖機制,都是無鎖化的CAS機制,所以他們倆可能會并發(fā)的同時執(zhí)行incrementAndGet()操作。
  • 然后倆人都獲取到了當(dāng)前AtomicInteger的值,就是1
  • 接著線程2搶先一步發(fā)起了原子的CAS操作!注意,CAS是原子的,此時就他一個線程在執(zhí)行!
  • 然后線程2問:兄弟,你現(xiàn)在值還是1嗎?如果是,太好了,說明沒人改過,我來改成2
  • 好了,此時AtomicInteger的值變?yōu)榱?。關(guān)鍵點來了:現(xiàn)在線程3接著發(fā)起了CAS操作,但是他手上還是拿著之前獲取到的那個1?。?/span>
  • 線程3此時會問問說:兄弟,你現(xiàn)在值還是1嗎?
  • 噩耗傳來!?。∵@個時候的值是2??!線程3哭泣了,他說,居然有人在這個期間改過值。算了,那我還是重新再獲取一次值吧,于是獲取到了最新的值,值為2。
  • 然后再次發(fā)起CAS操作,問問,現(xiàn)在值是2嗎?是的!太好了,沒人改,我抓緊改,此時AtomicInteger值變?yōu)?!

上述整個過程,就是所謂Atomic原子類的原理,沒有基于加鎖機制串行化,而是基于CAS機制:先獲取一個值,然后發(fā)起CAS,比較這個值被人改過沒?如果沒有,就更改值!這個CAS是原子的,別人不會打斷你!

通過這個機制,不需要加鎖這么重量級的機制,也可以用輕量級的方式實現(xiàn)多個線程安全的并發(fā)的修改某個數(shù)值。

五、Java 8對CAS機制的優(yōu)化

但是這個CAS有沒有問題呢?肯定是有的。比如說大量的線程同時并發(fā)修改一個AtomicInteger,可能有很多線程會不停的自旋,進入一個無限重復(fù)的循環(huán)中。

這些線程不停地獲取值,然后發(fā)起CAS操作,但是發(fā)現(xiàn)這個值被別人改過了,于是再次進入下一個循環(huán),獲取值,發(fā)起CAS操作又失敗了,再次進入下一個循環(huán)。

在大量線程高并發(fā)更新AtomicInteger的時候,這種問題可能會比較明顯,導(dǎo)致大量線程空循環(huán),自旋轉(zhuǎn),性能和效率都不是特別好。

于是,當(dāng)當(dāng)當(dāng)當(dāng),Java 8推出了一個新的類,LongAdder,他就是嘗試使用分段CAS以及自動分段遷移的方式來大幅度提升多線程高并發(fā)執(zhí)行CAS操作的性能!

在LongAdder的底層實現(xiàn)中,首先有一個base值,剛開始多線程來不停的累加數(shù)值,都是對base進行累加的,比如剛開始累加成了base = 5。

接著如果發(fā)現(xiàn)并發(fā)更新的線程數(shù)量過多,就會開始施行分段CAS的機制,也就是內(nèi)部會搞一個Cell數(shù)組,每個數(shù)組是一個數(shù)值分段。

這時,讓大量的線程分別去對不同Cell內(nèi)部的value值進行CAS累加操作,這樣就把CAS計算壓力分散到了不同的Cell分段數(shù)值中了!

這樣就可以大幅度的降低多線程并發(fā)更新同一個數(shù)值時出現(xiàn)的無限循環(huán)的問題,大幅度提升了多線程并發(fā)更新數(shù)值的性能和效率!

而且他內(nèi)部實現(xiàn)了自動分段遷移的機制,也就是如果某個Cell的value執(zhí)行CAS失敗了,那么就會自動去找另外一個Cell分段內(nèi)的value值進行CAS操作。

這樣也解決了線程空旋轉(zhuǎn)、自旋不停等待執(zhí)行CAS操作的問題,讓一個線程過來執(zhí)行CAS時可以盡快的完成這個操作。

最后,如果你要從LongAdder中獲取當(dāng)前累加的總值,就會把base值和所有Cell分段數(shù)值加起來返回給你。

六、總結(jié) & 思考

不知道大家有沒有發(fā)現(xiàn)這種高并發(fā)訪問下的分段處理機制,在很多地方都有類似的思想體現(xiàn)!因為高并發(fā)中的分段處理機制實際上是一個很常見和常用的并發(fā)優(yōu)化手段。

在我們之前的一篇講分布式鎖的文章:??為什么公司規(guī)定所有接口都必須加上分布式鎖,你知道嗎???,也是用到了分段加鎖以及自動分段遷移/合并加鎖的一套機制,來大幅度幾十倍的提升分布式鎖的并發(fā)性能。

所以其實很多技術(shù),思想都是有異曲同工之妙的。

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2019-01-02 07:36:28

微軟 Windows 程序員

2020-08-10 08:12:18

Java程序員開發(fā)

2014-03-04 09:43:23

程序員外包

2018-07-24 11:21:25

程序員加班月薪

2023-08-07 12:11:26

CPU存儲器SSD

2016-03-04 10:57:06

招聘程序員軟件

2024-08-30 09:51:06

JavaPython語言

2016-04-28 11:17:33

互動出版網(wǎng)

2011-08-11 14:52:59

2015-09-24 09:04:36

程序員

2011-09-18 09:42:08

程序員

2013-10-29 10:24:31

程序員漫畫

2015-06-05 14:15:13

程序員難升職

2013-04-09 09:54:34

程序員

2018-06-25 13:37:13

2009-05-26 09:00:59

ScalaJava面向?qū)ο?/a>

2014-08-15 11:07:09

程序員

2011-12-20 09:01:25

.NET

2012-09-06 10:30:58

2016-03-15 08:51:12

程序員生活怪異
點贊
收藏

51CTO技術(shù)棧公眾號