樂(lè)觀鎖與悲觀鎖

何謂悲觀鎖與樂(lè)觀鎖

樂(lè)觀鎖對(duì)應(yīng)于生活中樂(lè)觀的人總是想著事情往好的方向發(fā)展载萌,悲觀鎖對(duì)應(yīng)于生活中悲觀的人總是想著事情往壞的方向發(fā)展搏予。這兩種人各有優(yōu)缺點(diǎn)慎玖,不能不以場(chǎng)景而定說(shuō)一種人好于另外一種人句旱。

悲觀鎖

總是假設(shè)最壞的情況争占,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖闷袒,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)阻塞直到它拿到鎖(共享資源每次只給一個(gè)線程使用坑律,其它線程阻塞,用完后再把資源轉(zhuǎn)讓給其它線程)囊骤。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)里邊就用到了很多這種鎖機(jī)制晃择,比如行鎖,表鎖等也物,讀鎖宫屠,寫(xiě)鎖等,都是在做操作之前先上鎖焦除。Java中synchronizedReentrantLock等獨(dú)占鎖就是悲觀鎖思想的實(shí)現(xiàn)激况。

樂(lè)觀鎖

總是假設(shè)最好的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改膘魄,所以不會(huì)上鎖乌逐,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒(méi)有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)機(jī)制和CAS算法實(shí)現(xiàn)创葡。樂(lè)觀鎖適用于多讀的應(yīng)用類(lèi)型浙踢,這樣可以提高吞吐量,像數(shù)據(jù)庫(kù)提供的類(lèi)似于write_condition機(jī)制灿渴,其實(shí)都是提供的樂(lè)觀鎖洛波。在Java中java.util.concurrent.atomic包下面的原子變量類(lèi)就是使用了樂(lè)觀鎖的一種實(shí)現(xiàn)方式CAS實(shí)現(xiàn)的。

[]兩種鎖的使用場(chǎng)景

從上面對(duì)兩種鎖的介紹骚露,我們知道兩種鎖各有優(yōu)缺點(diǎn)蹬挤,不可認(rèn)為一種好于另一種,像樂(lè)觀鎖適用于寫(xiě)比較少的情況下(多讀場(chǎng)景)棘幸,即沖突真的很少發(fā)生的時(shí)候焰扳,這樣可以省去了鎖的開(kāi)銷(xiāo),加大了系統(tǒng)的整個(gè)吞吐量。但如果是多寫(xiě)的情況吨悍,一般會(huì)經(jīng)常產(chǎn)生沖突扫茅,這就會(huì)導(dǎo)致上層應(yīng)用會(huì)不斷的進(jìn)行retry,這樣反倒是降低了性能育瓜,所以一般多寫(xiě)的場(chǎng)景下用悲觀鎖就比較合適葫隙。

樂(lè)觀鎖常見(jiàn)的兩種實(shí)現(xiàn)方式

樂(lè)觀鎖一般會(huì)使用版本號(hào)機(jī)制或CAS算法實(shí)現(xiàn)。

版本號(hào)機(jī)制

一般是在數(shù)據(jù)表中加上一個(gè)數(shù)據(jù)版本號(hào)version字段躏仇,表示數(shù)據(jù)被修改的次數(shù)恋脚,當(dāng)數(shù)據(jù)被修改時(shí),version值會(huì)加一焰手。當(dāng)線程A要更新數(shù)據(jù)值時(shí)慧起,在讀取數(shù)據(jù)的同時(shí)也會(huì)讀取version值,在提交更新時(shí)册倒,若剛才讀取到的version值為當(dāng)前數(shù)據(jù)庫(kù)中的version值相等時(shí)才更新,否則重試更新操作磺送,直到更新成功驻子。

舉一個(gè)簡(jiǎn)單的例子: 假設(shè)數(shù)據(jù)庫(kù)中帳戶信息表中有一個(gè) version 字段,當(dāng)前值為 1 估灿;而當(dāng)前帳戶余額字段( balance )為 $100 崇呵。

  1. 操作員 A 此時(shí)將其讀出( version=1 ),并從其帳戶余額中扣除 50(100-$50 )馅袁。
  2. 在操作員 A 操作的過(guò)程中域慷,操作員B 也讀入此用戶信息( version=1 ),并從其帳戶余額中扣除 20 (100-$20 )汗销。
  3. 操作員 A 完成了修改工作犹褒,將數(shù)據(jù)版本號(hào)加一( version=2 ),連同帳戶扣除后余額( balance=$50 )弛针,提交至數(shù)據(jù)庫(kù)更新叠骑,此時(shí)由于提交數(shù)據(jù)版本大于數(shù)據(jù)庫(kù)記錄當(dāng)前版本,數(shù)據(jù)被更新削茁,數(shù)據(jù)庫(kù)記錄 version 更新為 2 宙枷。
  4. 操作員 B 完成了操作,也將版本號(hào)加一( version=2 )試圖向數(shù)據(jù)庫(kù)提交數(shù)據(jù)( balance=$80 )茧跋,但此時(shí)比對(duì)數(shù)據(jù)庫(kù)記錄版本時(shí)發(fā)現(xiàn)慰丛,操作員 B 提交的數(shù)據(jù)版本號(hào)為 2 ,數(shù)據(jù)庫(kù)記錄當(dāng)前版本也為 2 瘾杭,不滿足 “ 提交版本必須大于記錄當(dāng)前版本才能執(zhí)行更新 “ 的樂(lè)觀鎖策略诅病,因此,操作員 B 的提交被駁回。

這樣睬隶,就避免了操作員 B 用基于 version=1 的舊數(shù)據(jù)修改的結(jié)果覆蓋操作員A 的操作結(jié)果的可能锣夹。

2. CAS算法

compare and swap(比較與交換),是一種有名的無(wú)鎖算法苏潜。無(wú)鎖編程银萍,即不使用鎖的情況下實(shí)現(xiàn)多線程之間的變量同步,也就是在沒(méi)有線程被阻塞的情況下實(shí)現(xiàn)變量的同步恤左,所以也叫非阻塞同步(Non-blocking Synchronization)贴唇。CAS算法涉及到三個(gè)操作數(shù)

  • 需要讀寫(xiě)的內(nèi)存值 V
  • 進(jìn)行比較的值 A
  • 擬寫(xiě)入的新值 B

當(dāng)且僅當(dāng) V 的值等于 A時(shí),CAS通過(guò)原子方式用新值B來(lái)更新V的值飞袋,否則不會(huì)執(zhí)行任何操作(比較和替換是一個(gè)原子操作)戳气。一般情況下是一個(gè)自旋操作,即不斷的重試巧鸭。

關(guān)于自旋鎖瓶您,大家可以看一下這篇文章,非常不錯(cuò):《 面試必備之深入理解自旋鎖》

樂(lè)觀鎖的缺點(diǎn)

ABA 問(wèn)題是樂(lè)觀鎖一個(gè)常見(jiàn)的問(wèn)題

1 ABA 問(wèn)題

如果一個(gè)變量V初次讀取的時(shí)候是A值纲仍,并且在準(zhǔn)備賦值的時(shí)候檢查到它仍然是A值呀袱,那我們就能說(shuō)明它的值沒(méi)有被其他線程修改過(guò)了嗎?很明顯是不能的郑叠,因?yàn)樵谶@段時(shí)間它的值可能被改為其他值夜赵,然后又改回A,那CAS操作就會(huì)誤認(rèn)為它從來(lái)沒(méi)有被修改過(guò)乡革。這個(gè)問(wèn)題被稱為CAS操作的 "ABA"問(wèn)題寇僧。

JDK 1.5 以后的 AtomicStampedReference 類(lèi)就提供了此種能力,其中的 compareAndSet 方法就是首先檢查當(dāng)前引用是否等于預(yù)期引用沸版,并且當(dāng)前標(biāo)志是否等于預(yù)期標(biāo)志嘁傀,如果全部相等,則以原子方式將該引用和該標(biāo)志的值設(shè)置為給定的更新值视粮。

2 循環(huán)時(shí)間長(zhǎng)開(kāi)銷(xiāo)大

自旋CAS(也就是不成功就一直循環(huán)執(zhí)行直到成功)如果長(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è)共享變量的原子操作

CAS 只對(duì)單個(gè)共享變量有效炉爆,當(dāng)操作涉及跨多個(gè)共享變量時(shí) CAS 無(wú)效。但是從 JDK 1.5開(kāi)始,提供了AtomicReference類(lèi)來(lái)保證引用對(duì)象之間的原子性芬首,你可以把多個(gè)變量放在一個(gè)對(duì)象里來(lái)進(jìn)行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類(lèi)把多個(gè)共享變量合并成一個(gè)共享變量來(lái)操作赴捞。

CAS與synchronized的使用情景

簡(jiǎn)單的來(lái)說(shuō)CAS適用于寫(xiě)比較少的情況下(多讀場(chǎng)景,沖突一般較少)郁稍,synchronized適用于寫(xiě)比較多的情況下(多寫(xiě)場(chǎng)景赦政,沖突一般較多)

  1. 對(duì)于資源競(jìng)爭(zhēng)較少(線程沖突較輕)的情況,使用synchronized同步鎖進(jìn)行線程阻塞和喚醒切換以及用戶態(tài)內(nèi)核態(tài)間的切換操作額外浪費(fèi)消耗cpu資源耀怜;而CAS基于硬件實(shí)現(xiàn)恢着,不需要進(jìn)入內(nèi)核,不需要切換線程财破,操作自旋幾率較少掰派,因此可以獲得更高的性能。
  2. 對(duì)于資源競(jìng)爭(zhēng)嚴(yán)重(線程沖突嚴(yán)重)的情況左痢,CAS自旋的概率會(huì)比較大靡羡,從而浪費(fèi)更多的CPU資源,效率低于synchronized俊性。

補(bǔ)充: Java并發(fā)編程這個(gè)領(lǐng)域中synchronized關(guān)鍵字一直都是元老級(jí)的角色亿眠,很久之前很多人都會(huì)稱它為 “重量級(jí)鎖” 。但是磅废,在JavaSE 1.6之后進(jìn)行了主要包括為了減少獲得鎖和釋放鎖帶來(lái)的性能消耗而引入的 偏向鎖輕量級(jí)鎖 以及其它各種優(yōu)化之后變得在某些情況下并不是那么重了。synchronized的底層實(shí)現(xiàn)主要依靠 Lock-Free 的隊(duì)列荆烈,基本思路是 自旋后阻塞拯勉,競(jìng)爭(zhēng)切換后繼續(xù)競(jìng)爭(zhēng)鎖稍微犧牲了公平性憔购,但獲得了高吞吐量宫峦。在線程沖突較少的情況下,可以獲得和CAS類(lèi)似的性能玫鸟;而線程沖突嚴(yán)重的情況下导绷,性能遠(yuǎn)高于CAS。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末屎飘,一起剝皮案震驚了整個(gè)濱河市妥曲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌钦购,老刑警劉巖檐盟,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異押桃,居然都是意外死亡葵萎,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)羡忘,“玉大人谎痢,你說(shuō)我怎么就攤上這事【淼瘢” “怎么了节猿?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)爽蝴。 經(jīng)常有香客問(wèn)我沐批,道長(zhǎng),這世上最難降的妖魔是什么蝎亚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任九孩,我火速辦了婚禮,結(jié)果婚禮上发框,老公的妹妹穿的比我還像新娘躺彬。我一直安慰自己,他們只是感情好梅惯,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布宪拥。 她就那樣靜靜地躺著,像睡著了一般铣减。 火紅的嫁衣襯著肌膚如雪她君。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,255評(píng)論 1 308
  • 那天葫哗,我揣著相機(jī)與錄音缔刹,去河邊找鬼。 笑死劣针,一個(gè)胖子當(dāng)著我的面吹牛校镐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捺典,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鸟廓,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了襟己?” 一聲冷哼從身側(cè)響起引谜,我...
    開(kāi)封第一講書(shū)人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎擎浴,沒(méi)想到半個(gè)月后煌张,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡退客,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年骏融,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了链嘀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡档玻,死狀恐怖怀泊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情误趴,我是刑警寧澤霹琼,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站凉当,受9級(jí)特大地震影響枣申,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜看杭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一忠藤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧楼雹,春花似錦模孩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至谴供,卻和暖如春块茁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背桂肌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工数焊, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轴或。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像仰禀,于是被迫代替她去往敵國(guó)和親照雁。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359