鎖4

目前在Java中存在兩種鎖機(jī)制:synchronized和Lock盐茎,Lock接口及其實(shí)現(xiàn)類是JDK5增加的內(nèi)容。本文并不比較synchronized與Lock孰優(yōu)孰劣,只是介紹二者的實(shí)現(xiàn)原理。

13、偏向鎖和輕量級(jí)鎖澜术、鎖粗化、鎖消除猬腰、鎖膨脹

因?yàn)檫@幾個(gè)概念連續(xù)非常緊密所以放在一起會(huì)方便理解記憶鸟废。

在jdk1.6中對(duì)鎖的實(shí)現(xiàn)引入了大量的優(yōu)化,如鎖粗化(Lock Coarsening)姑荷、鎖消除(Lock Elimination)盒延、輕量級(jí)鎖(Lightweight Locking)、

偏向鎖(Biased Locking)鼠冕、適應(yīng)性自旋(Adaptive Spinning)等技術(shù)來(lái)減少鎖操作的開(kāi)銷(xiāo)添寺。

鎖粗化(Lock Coarsening):也就是減少不必要的緊連在一起的unlock,lock操作懈费,將多個(gè)連續(xù)的鎖擴(kuò)展成一個(gè)范圍更大的鎖计露。

鎖消除(Lock Elimination):通過(guò)運(yùn)行時(shí)JIT編譯器的逃逸分析來(lái)消除一些沒(méi)有在當(dāng)前同步塊以外被其他線程共享的數(shù)據(jù)的鎖保護(hù),

通過(guò)逃逸分析也可以在線程本地Stack上進(jìn)行對(duì)象空間的分配(同時(shí)還可以減少Heap上的垃圾收集開(kāi)銷(xiāo))憎乙。

輕量級(jí)鎖(Lightweight Locking):這種鎖實(shí)現(xiàn)的背后基于這樣一種假設(shè)票罐,即在真實(shí)的情況下我們程序中的大部分同步代碼一般都處于無(wú)鎖競(jìng)爭(zhēng)狀態(tài)

(即單線程執(zhí)行環(huán)境),在無(wú)鎖競(jìng)爭(zhēng)的情況下完全可以避免調(diào)用操作系統(tǒng)層面的重量級(jí)互斥鎖泞边,

取而代之的是在monitorenter和monitorexit中只需要依靠一條CAS原子指令就可以完成鎖的獲取及釋放该押。

當(dāng)存在鎖競(jìng)爭(zhēng)的情況下,執(zhí)行CAS指令失敗的線程將調(diào)用操作系統(tǒng)互斥鎖進(jìn)入到阻塞狀態(tài)阵谚,當(dāng)鎖被釋放的時(shí)候被喚醒(具體處理步驟下面詳細(xì)討論)蚕礼。

偏向鎖(Biased Locking):是為了在無(wú)鎖競(jìng)爭(zhēng)的情況下避免在鎖獲取過(guò)程中執(zhí)行不必要的CAS原子指令,

因?yàn)镃AS原子指令雖然相對(duì)于重量級(jí)鎖來(lái)說(shuō)開(kāi)銷(xiāo)比較小但還是存在非成沂玻可觀的本地延遲(可參考這篇文章)奠蹬。

適應(yīng)性自旋(Adaptive Spinning):當(dāng)線程在獲取輕量級(jí)鎖的過(guò)程中執(zhí)行CAS操作失敗時(shí),在進(jìn)入與monitor相關(guān)聯(lián)的操作系統(tǒng)重量級(jí)鎖

(mutex semaphore)前會(huì)進(jìn)入忙等待(Spinning)然后再次嘗試嗡午,當(dāng)嘗試一定的次數(shù)后如果仍然沒(méi)有成功則調(diào)用與該monitor關(guān)聯(lián)的semaphore(即互斥鎖)囤躁,

進(jìn)入到阻塞狀態(tài)。

注:(適應(yīng)性)自旋鎖,是在從輕量級(jí)鎖向重量級(jí)鎖膨脹的過(guò)程中使用的割以,是在進(jìn)入重量級(jí)鎖之前進(jìn)行的金度。

鎖存在Java對(duì)象頭里应媚。如果對(duì)象是數(shù)組類型严沥,則虛擬機(jī)用3個(gè)Word(字寬)存儲(chǔ)對(duì)象頭,如果對(duì)象是非數(shù)組類型中姜,則用2字寬存儲(chǔ)對(duì)象頭消玄。

在32位虛擬機(jī)中,一字寬等于四字節(jié)丢胚,即32bit翩瓜。

鎖狀態(tài)包括:輕量級(jí)鎖定、重量級(jí)鎖定携龟、GC標(biāo)記兔跌、可偏向

簡(jiǎn)單的加鎖機(jī)制:

機(jī)制:每個(gè)鎖都關(guān)聯(lián)一個(gè)請(qǐng)求計(jì)數(shù)器和一個(gè)占有他的線程,當(dāng)請(qǐng)求計(jì)數(shù)器為0時(shí)峡蟋,這個(gè)鎖可以被認(rèn)為是unhled的坟桅,

當(dāng)一個(gè)線程請(qǐng)求一個(gè)unheld的鎖時(shí),JVM記錄鎖的擁有者蕊蝗,并把鎖的請(qǐng)求計(jì)數(shù)加1仅乓,如果同一個(gè)線程再次請(qǐng)求這個(gè)鎖時(shí),請(qǐng)求計(jì)數(shù)器就會(huì)增加蓬戚,

當(dāng)該線程退出syncronized塊時(shí)夸楣,計(jì)數(shù)器減1,當(dāng)計(jì)數(shù)器為0時(shí)子漩,鎖被釋放(這就保證了鎖是可重入的豫喧,不會(huì)發(fā)生死鎖的情況)。

偏向鎖流程:

偏向鎖幢泼,簡(jiǎn)單的講嘿棘,就是在鎖對(duì)象的對(duì)象頭中有個(gè)ThreaddId字段,這個(gè)字段如果是空的旭绒,

第一次獲取鎖的時(shí)候鸟妙,就將自身的ThreadId寫(xiě)入到鎖的ThreadId字段內(nèi),將鎖頭內(nèi)的是否偏向鎖的狀態(tài)位置1.

這樣下次獲取鎖的時(shí)候挥吵,直接檢查T(mén)hreadId是否和自身線程Id一致重父,如果一致,則認(rèn)為當(dāng)前線程已經(jīng)獲取了鎖忽匈,因此不需再次獲取鎖房午,

略過(guò)了輕量級(jí)鎖和重量級(jí)鎖的加鎖階段。提高了效率丹允。

但是偏向鎖也有一個(gè)問(wèn)題郭厌,就是當(dāng)鎖有競(jìng)爭(zhēng)關(guān)系的時(shí)候袋倔,需要解除偏向鎖,使鎖進(jìn)入競(jìng)爭(zhēng)的狀態(tài)折柠。

下面是清晰的流程:

上圖中只講了偏向鎖的釋放宾娜,其實(shí)還涉及偏向鎖的搶占,其實(shí)就是兩個(gè)進(jìn)程對(duì)鎖的搶占扇售,在synchrnized鎖下表現(xiàn)為輕量鎖方式進(jìn)行搶占前塔。

注:也就是說(shuō)一旦偏向鎖沖突,雙方都會(huì)升級(jí)為輕量級(jí)鎖承冰。(這一點(diǎn)與輕量級(jí)->重量級(jí)鎖不同华弓,那時(shí)候失敗一方直接升級(jí),成功一方在釋放時(shí)候notify困乒,加下文后面詳細(xì)描述)

如下圖寂屏。之后會(huì)進(jìn)入到輕量級(jí)鎖階段,兩個(gè)線程進(jìn)入鎖競(jìng)爭(zhēng)狀態(tài)(注娜搂,我理解仍然會(huì)遵守先來(lái)后到原則迁霎;注2,的確是的涌攻,下圖中提到了mark word中的lock record指向堆棧中最近的一個(gè)線程的lock record)欧引,一個(gè)具體例子可以參考synchronized鎖機(jī)制。(圖后面有介紹)

每一個(gè)線程在準(zhǔn)備獲取共享資源時(shí):

第一步恳谎,檢查MarkWord里面是不是放的自己的ThreadId ,如果是芝此,表示當(dāng)前線程是處于 “偏向鎖”

第二步,如果MarkWord不是自己的ThreadId,鎖升級(jí)因痛,這時(shí)候婚苹,用CAS來(lái)執(zhí)行切換,新的線程根據(jù)MarkWord里面現(xiàn)有的ThreadId鸵膏,通知之前線程暫停膊升,

之前線程將Markword的內(nèi)容置為空。

第三步谭企,兩個(gè)線程都把對(duì)象的HashCode復(fù)制到自己新建的用于存儲(chǔ)鎖的記錄空間廓译,接著開(kāi)始通過(guò)CAS操作,

把共享對(duì)象的MarKword的內(nèi)容修改為自己新建的記錄空間的地址的方式競(jìng)爭(zhēng)MarkWord,

第四步债查,第三步中成功執(zhí)行CAS的獲得資源非区,失敗的則進(jìn)入自旋

第五步,自旋的線程在自旋過(guò)程中盹廷,成功獲得資源(即之前獲的資源的線程執(zhí)行完成并釋放了共享資源)征绸,則整個(gè)狀態(tài)依然處于 輕量級(jí)鎖的狀態(tài),如果自旋失敗

第六步,進(jìn)入重量級(jí)鎖的狀態(tài)管怠,這個(gè)時(shí)候淆衷,自旋的線程進(jìn)行阻塞,等待之前線程執(zhí)行完成并喚醒自己

復(fù)制代碼

總結(jié):

偏向鎖渤弛,其實(shí)是無(wú)鎖競(jìng)爭(zhēng)下可重入鎖的簡(jiǎn)單實(shí)現(xiàn)祝拯。流程是這樣的 偏向鎖->輕量級(jí)鎖->重量級(jí)鎖

同步的原理

JVM規(guī)范規(guī)定JVM基于進(jìn)入和退出Monitor對(duì)象來(lái)實(shí)現(xiàn)方法同步和代碼塊同步,但兩者的實(shí)現(xiàn)細(xì)節(jié)不一樣暮芭。

代碼塊同步是使用monitorenter和monitorexit指令實(shí)現(xiàn)鹿驼,而方法同步是使用另外一種方式實(shí)現(xiàn)的欲低,細(xì)節(jié)在JVM規(guī)范里并沒(méi)有詳細(xì)說(shuō)明辕宏,但是方法的同步同樣可以使用這兩個(gè)指令來(lái)實(shí)現(xiàn)。

monitorenter指令是在編譯后插入到同步代碼塊的開(kāi)始位置砾莱,而monitorexit是插入到方法結(jié)束處和異常處瑞筐, JVM要保證每個(gè)monitorenter必須有對(duì)應(yīng)的monitorexit與之配對(duì)。

任何對(duì)象都有一個(gè) monitor 與之關(guān)聯(lián)腊瑟,當(dāng)且一個(gè)monitor 被持有后聚假,它將處于鎖定狀態(tài)。線程執(zhí)行到 monitorenter 指令時(shí)闰非,將會(huì)嘗試獲取對(duì)象所對(duì)應(yīng)的 monitor 的所有權(quán)膘格,即嘗試獲得對(duì)象的鎖。

Java對(duì)象頭

鎖存在Java對(duì)象頭里财松。如果對(duì)象是數(shù)組類型瘪贱,則虛擬機(jī)用3個(gè)Word(字寬)存儲(chǔ)對(duì)象頭,如果對(duì)象是非數(shù)組類型辆毡,則用2字寬存儲(chǔ)對(duì)象頭菜秦。在32位虛擬機(jī)中,一字寬等于四字節(jié)舶掖,即32bit球昨。(下面這個(gè)表格講的很清楚)

Java對(duì)象頭里的Mark Word里默認(rèn)存儲(chǔ)對(duì)象的HashCode,分代年齡和鎖標(biāo)記位眨攘。32位JVM的Mark Word的默認(rèn)存儲(chǔ)結(jié)構(gòu)如下:

在運(yùn)行期間Mark Word里存儲(chǔ)的數(shù)據(jù)會(huì)隨著鎖標(biāo)志位的變化而變化主慰。Mark Word可能變化為存儲(chǔ)以下4種數(shù)據(jù):

上圖里面的GC標(biāo)記,為11的話鲫售,推斷應(yīng)該是準(zhǔn)備GC的意思共螺。

在64位虛擬機(jī)下,Mark Word是64bit大小的龟虎,其存儲(chǔ)結(jié)構(gòu)如下:

鎖的升級(jí)

Java SE1.6為了減少獲得鎖和釋放鎖所帶來(lái)的性能消耗璃谨,引入了“偏向鎖”和“輕量級(jí)鎖”,

所以在Java SE1.6里鎖一共有四種狀態(tài),無(wú)鎖狀態(tài)佳吞,偏向鎖狀態(tài)拱雏,輕量級(jí)鎖狀態(tài)和重量級(jí)鎖狀態(tài),它會(huì)隨著競(jìng)爭(zhēng)情況逐漸升級(jí)底扳。

鎖可以升級(jí)但不能降級(jí)铸抑,意味著偏向鎖升級(jí)成輕量級(jí)鎖后不能降級(jí)成偏向鎖。

這種鎖升級(jí)卻不能降級(jí)的策略衷模,目的是為了提高獲得鎖和釋放鎖的效率鹊汛,下文會(huì)詳細(xì)分析。

偏向鎖

復(fù)制代碼

Hotspot的作者經(jīng)過(guò)以往的研究發(fā)現(xiàn)大多數(shù)情況下鎖不僅不存在多線程競(jìng)爭(zhēng)阱冶,而且總是由同一線程多次獲得刁憋,為了讓線程獲得鎖的代價(jià)更低而引入了偏向鎖。

當(dāng)一個(gè)線程訪問(wèn)同步塊并獲取鎖時(shí)木蹬,會(huì)在對(duì)象頭和棧幀中的鎖記錄里存儲(chǔ)鎖偏向的線程ID至耻,

以后該線程在進(jìn)入和退出同步塊時(shí)不需要花費(fèi)CAS操作來(lái)加鎖和解鎖,而只需簡(jiǎn)單的測(cè)試一下對(duì)象頭的Mark Word里是否存儲(chǔ)著指向當(dāng)前線程的偏向鎖镊叁,

如果測(cè)試成功尘颓,表示線程已經(jīng)獲得了鎖,如果測(cè)試失敗晦譬,則需要再測(cè)試下Mark Word中偏向鎖的標(biāo)識(shí)是否設(shè)置成1(表示當(dāng)前是偏向鎖)疤苹,如果沒(méi)有設(shè)置,

則使用CAS競(jìng)爭(zhēng)鎖敛腌,如果設(shè)置了卧土,則嘗試使用CAS將對(duì)象頭的偏向鎖指向當(dāng)前線程。

偏向鎖的撤銷(xiāo):偏向鎖使用了一種等到競(jìng)爭(zhēng)出現(xiàn)才釋放鎖的機(jī)制迎瞧,所以當(dāng)其他線程嘗試競(jìng)爭(zhēng)偏向鎖時(shí)夸溶,持有偏向鎖的線程才會(huì)釋放鎖。

偏向鎖的撤銷(xiāo)凶硅,需要等待全局安全點(diǎn)(在這個(gè)時(shí)間點(diǎn)上沒(méi)有字節(jié)碼正在執(zhí)行)缝裁,

它會(huì)首先暫停擁有偏向鎖的線程,然后檢查持有偏向鎖的線程是否活著足绅,

如果線程不處于活動(dòng)狀態(tài)捷绑,則將對(duì)象頭設(shè)置成無(wú)鎖狀態(tài),

如果線程仍然活著氢妈,擁有偏向鎖的棧會(huì)被執(zhí)行粹污,遍歷偏向?qū)ο蟮逆i記錄,

棧中的鎖記錄和對(duì)象頭的Mark Word要么重新偏向于其他線程首量,要么恢復(fù)到無(wú)鎖或者標(biāo)記對(duì)象不適合作為偏向鎖壮吩,最后喚醒暫停的線程进苍。

上面的意思是,先暫停持有偏向鎖的線程鸭叙,嘗試直接切換觉啊。如果不成功,就繼續(xù)運(yùn)行沈贝,并且標(biāo)記對(duì)象不適合偏向鎖杠人,鎖膨脹(鎖升級(jí))。

詳見(jiàn)宋下,上面有張圖中的“偏向鎖搶占模式”:

其中提到了mark word中的lock record指向堆棧最近的一個(gè)線程的lock record嗡善,其實(shí)就是按照先來(lái)后到模式進(jìn)行了輕量級(jí)的加鎖。

復(fù)制代碼

上文提到全局安全點(diǎn):在這個(gè)時(shí)間點(diǎn)上沒(méi)有字節(jié)碼正在執(zhí)行学歧。

關(guān)閉偏向鎖:偏向鎖在Java 6和Java 7里是默認(rèn)啟用的罩引,但是它在應(yīng)用程序啟動(dòng)幾秒鐘之后才激活,

如有必要可以使用JVM參數(shù)來(lái)關(guān)閉延遲-XX:BiasedLockingStartupDelay = 0撩满。

如果你確定自己應(yīng)用程序里所有的鎖通常情況下處于競(jìng)爭(zhēng)狀態(tài)蜒程,可以通過(guò)JVM參數(shù)關(guān)閉偏向鎖-XX:-UseBiasedLocking=false绅你,那么默認(rèn)會(huì)進(jìn)入輕量級(jí)鎖狀態(tài)伺帘。

輕量級(jí)鎖

輕量級(jí)鎖加鎖:線程在執(zhí)行同步塊之前,JVM會(huì)先在當(dāng)前線程的棧楨中創(chuàng)建用于存儲(chǔ)鎖記錄的空間忌锯,并將對(duì)象頭中的Mark Word復(fù)制到鎖記錄中伪嫁,官方稱為Displaced Mark Word。

然后線程嘗試使用CAS將對(duì)象頭中的Mark Word替換為指向鎖記錄的指針偶垮。如果成功张咳,當(dāng)前線程獲得鎖,如果失敗似舵,表示其他線程競(jìng)爭(zhēng)鎖脚猾,當(dāng)前線程便嘗試使用自旋來(lái)獲取鎖。

輕量級(jí)鎖解鎖:輕量級(jí)解鎖時(shí)砚哗,會(huì)使用原子的CAS操作來(lái)將Displaced Mark Word替換回到對(duì)象頭龙助,如果成功,則表示沒(méi)有競(jìng)爭(zhēng)發(fā)生蛛芥。

如果失敗提鸟,表示當(dāng)前鎖存在競(jìng)爭(zhēng),鎖就會(huì)膨脹成重量級(jí)鎖仅淑。

注:輕量級(jí)鎖會(huì)一直保持称勋,喚醒總是發(fā)生在輕量級(jí)鎖解鎖的時(shí)候,因?yàn)榧渔i的時(shí)候已經(jīng)成功CAS操作涯竟;而CAS失敗的線程赡鲜,會(huì)立即鎖膨脹空厌,并阻塞等待喚醒。(詳見(jiàn)下圖)

下圖是兩個(gè)線程同時(shí)爭(zhēng)奪鎖银酬,導(dǎo)致鎖膨脹的流程圖蝇庭。

鎖不會(huì)降級(jí)

因?yàn)樽孕龝?huì)消耗CPU,為了避免無(wú)用的自旋(比如獲得鎖的線程被阻塞住了)捡硅,一旦鎖升級(jí)成重量級(jí)鎖哮内,就不會(huì)再恢復(fù)到輕量級(jí)鎖狀態(tài)。

當(dāng)鎖處于這個(gè)狀態(tài)下壮韭,其他線程試圖獲取鎖時(shí)北发,都會(huì)被阻塞住,當(dāng)持有鎖的線程釋放鎖之后會(huì)喚醒這些線程喷屋,被喚醒的線程就會(huì)進(jìn)行新一輪的奪鎖之爭(zhēng)琳拨。

輕量級(jí)鎖具體實(shí)現(xiàn):

一個(gè)線程能夠通過(guò)兩種方式鎖住一個(gè)對(duì)象:1、通過(guò)膨脹一個(gè)處于無(wú)鎖狀態(tài)(狀態(tài)位001)的對(duì)象獲得該對(duì)象的鎖屯曹;

2狱庇、對(duì)象已經(jīng)處于膨脹狀態(tài)(狀態(tài)位00)但LockWord指向的monitor record的Owner字段為NULL,

則可以直接通過(guò)CAS原子指令嘗試將Owner設(shè)置為自己的標(biāo)識(shí)來(lái)獲得鎖恶耽。

從中可以看出密任,是先檢查鎖的標(biāo)識(shí)位。

CAS應(yīng)用

CAS有3個(gè)操作數(shù)偷俭,內(nèi)存值V浪讳,舊的預(yù)期值A(chǔ),要修改的新值B涌萤。當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí)淹遵,將內(nèi)存值V修改為B,否則什么都不做负溪。

復(fù)制代碼

下面從分析比較常用的CPU(intel x86)來(lái)解釋CAS的實(shí)現(xiàn)原理透揣。

下面是sun.misc.Unsafe類的compareAndSwapInt()方法的源代碼:

public final native boolean compareAndSwapInt(Object o, long offset,

int expected,

int x);

可以看到這是個(gè)本地方法調(diào)用。這個(gè)本地方法在openjdk中依次調(diào)用的c++代碼為:unsafe.cpp川抡,atomic.cpp和atomicwindowsx86.inline.hpp辐真。

復(fù)制代碼

對(duì)于32位/64位的操作應(yīng)該是原子的:

奔騰6和最新的處理器能自動(dòng)保證單處理器對(duì)同一個(gè)緩存行里進(jìn)行16/32/64位的操作是原子的,但是復(fù)雜的內(nèi)存操作處理器不能自動(dòng)保證其原子性猖腕,

比如跨總線寬度拆祈,跨多個(gè)緩存行,跨頁(yè)表的訪問(wèn)倘感。但是處理器提供總線鎖定和緩存鎖定兩個(gè)機(jī)制來(lái)保證復(fù)雜內(nèi)存操作的原子性放坏。

CAS的缺點(diǎn)

復(fù)制代碼

CAS雖然很高效的解決原子操作,但是CAS仍然存在三大問(wèn)題老玛。ABA問(wèn)題淤年,循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大和只能保證一個(gè)共享變量的原子操作

1.? ABA問(wèn)題钧敞。因?yàn)镃AS需要在操作值的時(shí)候檢查下值有沒(méi)有發(fā)生變化,如果沒(méi)有發(fā)生變化則更新麸粮,但是如果一個(gè)值原來(lái)是A溉苛,變成了B,又變成了A弄诲,

那么使用CAS進(jìn)行檢查時(shí)會(huì)發(fā)現(xiàn)它的值沒(méi)有發(fā)生變化愚战,但是實(shí)際上卻變化了。ABA問(wèn)題的解決思路就是使用版本號(hào)齐遵。

在變量前面追加上版本號(hào)寂玲,每次變量更新的時(shí)候把版本號(hào)加一,那么A-B-A 就會(huì)變成1A-2B-3A梗摇。

從Java1.5開(kāi)始JDK的atomic包里提供了一個(gè)類AtomicStampedReference來(lái)解決ABA問(wèn)題拓哟。

這個(gè)類的compareAndSet方法作用是首先檢查當(dāng)前引用是否等于預(yù)期引用,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志伶授,如果全部相等断序,

則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值。

關(guān)于ABA問(wèn)題參考文檔: http://blog.hesey.net/2011/09/resolve-aba-by-atomicstampedreference.html

2. 循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大糜烹。自旋CAS如果長(zhǎng)時(shí)間不成功违诗,會(huì)給CPU帶來(lái)非常大的執(zhí)行開(kāi)銷(xiāo)。如果JVM能支持處理器提供的pause指令那么效率會(huì)有一定的提升景图,

pause指令有兩個(gè)作用较雕,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會(huì)消耗過(guò)多的執(zhí)行資源,

延遲的時(shí)間取決于具體實(shí)現(xiàn)的版本挚币,在一些處理器上延遲時(shí)間是零。

第二它可以避免在退出循環(huán)的時(shí)候因內(nèi)存順序沖突(memory order violation)而引起CPU流水線被清空(CPU pipeline flush)扣典,從而提高CPU的執(zhí)行效率妆毕。

3. 只能保證一個(gè)共享變量的原子操作。當(dāng)對(duì)一個(gè)共享變量執(zhí)行操作時(shí)贮尖,我們可以使用循環(huán)CAS的方式來(lái)保證原子操作笛粘,

但是對(duì)多個(gè)共享變量操作時(shí),循環(huán)CAS就無(wú)法保證操作的原子性湿硝,這個(gè)時(shí)候就可以用鎖薪前,

或者有一個(gè)取巧的辦法,就是把多個(gè)共享變量合并成一個(gè)共享變量來(lái)操作关斜。比如有兩個(gè)共享變量i=2,j=a示括,合并一下ij=2a,然后用CAS來(lái)操作ij痢畜。

從Java1.5開(kāi)始JDK提供了AtomicReference類來(lái)保證引用對(duì)象之間的原子性垛膝,你可以把多個(gè)變量放在一個(gè)對(duì)象里來(lái)進(jìn)行CAS操作鳍侣。

轉(zhuǎn)載自http://www.cnblogs.com/charlesblc/p/5994162.html這邊博客寫(xiě)的太好了!只是調(diào)整了排版然后刪除了一些冗余段落直接復(fù)用了吼拥!這都是滿滿的干貨耙芯邸!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末凿可,一起剝皮案震驚了整個(gè)濱河市惑折,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌枯跑,老刑警劉巖唬复,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異全肮,居然都是意外死亡敞咧,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)辜腺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)休建,“玉大人,你說(shuō)我怎么就攤上這事评疗〔馍埃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵百匆,是天一觀的道長(zhǎng)砌些。 經(jīng)常有香客問(wèn)我,道長(zhǎng)加匈,這世上最難降的妖魔是什么存璃? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮雕拼,結(jié)果婚禮上纵东,老公的妹妹穿的比我還像新娘。我一直安慰自己啥寇,他們只是感情好偎球,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著辑甜,像睡著了一般衰絮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上磷醋,一...
    開(kāi)封第一講書(shū)人閱讀 51,590評(píng)論 1 305
  • 那天猫牡,我揣著相機(jī)與錄音,去河邊找鬼子檀。 笑死镊掖,一個(gè)胖子當(dāng)著我的面吹牛乃戈,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播亩进,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼症虑,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了归薛?” 一聲冷哼從身側(cè)響起谍憔,我...
    開(kāi)封第一講書(shū)人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎主籍,沒(méi)想到半個(gè)月后习贫,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡千元,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年苫昌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片幸海。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡祟身,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出物独,到底是詐尸還是另有隱情袜硫,我是刑警寧澤,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布挡篓,位于F島的核電站婉陷,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏官研。R本人自食惡果不足惜秽澳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阀参。 院中可真熱鬧肝集,春花似錦、人聲如沸蛛壳。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)衙荐。三九已至,卻和暖如春浮创,著一層夾襖步出監(jiān)牢的瞬間忧吟,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工斩披, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留溜族,地道東北人讹俊。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像煌抒,于是被迫代替她去往敵國(guó)和親仍劈。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容