最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

請說一下悲觀鎖和樂觀鎖的區(qū)別

2022-04-17 13:15 作者:指南針畢業(yè)設(shè)計  | 我要投稿

悲觀鎖和樂觀鎖并不是某個具體的“鎖”而是一種并發(fā)編程的基本概念,是根據(jù)看待并發(fā)同步的角度。樂觀鎖和悲觀鎖最早出現(xiàn)在數(shù)據(jù)庫的設(shè)計當中,后來逐漸被 Java 的并發(fā)包所引入。

悲觀鎖

悲觀鎖認為對于同一個數(shù)據(jù)的并發(fā)操作一定是會發(fā)生修改的,采取加鎖的形式,悲觀地認為,不加鎖的并發(fā)操作一定會出問題。傳統(tǒng)的關(guān)系型數(shù)據(jù)庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。Java中Synchronized和ReentrantLock等獨占鎖就是悲觀鎖思想實現(xiàn)的。

樂觀鎖

樂觀鎖正好和悲觀鎖相反,它獲取數(shù)據(jù)的時候,并不擔心數(shù)據(jù)被修改,每次獲取數(shù)據(jù)的時候也不會加鎖,只是在更新數(shù)據(jù)的時候,通過判斷現(xiàn)有的數(shù)據(jù)是否和原數(shù)據(jù)一致來判斷數(shù)據(jù)是否被其他線程操作,如果沒被其他線程修改則進行數(shù)據(jù)更新,如果被其他線程修改則不進行數(shù)據(jù)更新。Lock?是樂觀鎖的典型實現(xiàn)案例。

典型案例

(1)悲觀鎖:synchronized 關(guān)鍵字和 Lock 接口相關(guān)類

Java 中悲觀鎖的實現(xiàn)包括 synchronized 關(guān)鍵字和 Lock 相關(guān)類等,我們以 Lock 接口為例,例如 Lock 的實現(xiàn)類 ReentrantLock,類中的 lock() 等方法就是執(zhí)行加鎖,而 unlock() 方法是執(zhí)行解鎖。處理資源之前必須要先加鎖并拿到鎖,等到處理完了之后再解開鎖,這就是非常典型的悲觀鎖思想。

(2)樂觀鎖:原子類

樂觀鎖的典型案例就是原子類,例如 AtomicInteger 在更新數(shù)據(jù)時,就使用了樂觀鎖的思想,多個線程可以同時操作同一個原子變量。

(3)大喜大悲:數(shù)據(jù)庫

數(shù)據(jù)庫中同時擁有悲觀鎖和樂觀鎖的思想。例如,我們?nèi)绻?MySQL 選擇 select for update 語句,那就是悲觀鎖,在提交之前不允許第三方來修改該數(shù)據(jù),這當然會造成一定的性能損耗,在高并發(fā)的情況下是不可取的。相反,我們可以利用一個版本 version 字段在數(shù)據(jù)庫中實現(xiàn)樂觀鎖。在獲取及修改數(shù)據(jù)時都不需要加鎖,但是我們在獲取完數(shù)據(jù)并計算完畢,準備更新數(shù)據(jù)時,會檢查版本號和獲取數(shù)據(jù)時的版本號是否一致,如果一致就直接更新,如果不一致,說明計算期間已經(jīng)有其他線程修改過這個數(shù)據(jù)了,那我就可以選擇重新獲取數(shù)據(jù),重新計算,然后再次嘗試更新數(shù)據(jù)。

數(shù)據(jù)庫的悲觀鎖示例:

select * from account where name="Erica" for update

這條?sql 語句鎖定了 account 表中所有符合檢索條件( name="Erica" )的記錄。本次事務(wù)提交之前(事務(wù)提交時會釋放事務(wù)過程中的鎖),外界無法修改這些記錄.。

數(shù)據(jù)庫的樂觀鎖示例:

假設(shè)取出數(shù)據(jù)的時候 version 為1

UPDATE?student

SET

name=‘小李’,

version=?2WHERE???id=?100AND?version=?1

CAS介紹

Java中的樂觀鎖大部分都是通過CAS(CompareAndSwap,比較并交換)操作實現(xiàn)的,CAS是一個多線程同步的原子指令,CAS操作包含三個重要的信息,即內(nèi)存位置、預期原值和新值。如果內(nèi)存位置的值和預期的原值相等的話,那么就可以把該位置的值更新為新值,否則不做任何修改。

補充說明: 雖然ReentrantLock也是通過CAS實現(xiàn)的,但是是悲觀鎖。

缺點:ABA問題.

CAS可能會造成ABA的問題,ABA問題指的是,線程拿到了最初的預期原值A(chǔ),然而在將要進行CAS的時候,被其他線程搶占了執(zhí)行權(quán),把此值從A變成了B,然后其他線程又把此值從B變成A,然而此時的 A 值已經(jīng)并非原來的 A 值了,但最初的線程并不知道這個情況,在它進行 CAS 的時候,就會誤認為它從來沒有被修改過,只對比了預期原值為 A 就進行了修改,這就造成了 ABA 的問題。

以警匪劇為例,假如某人把裝了100W現(xiàn)金的箱子放在了家里,幾分鐘之后要拿它去贖人,然而在趁他不注意的時候,進來了一個小偷,用空箱子換走了裝滿錢的箱子,當某人進來之后看到箱子還是一模一樣的,他會以為這就是原來的箱子,就拿著它去贖人了,這種情況肯定有問題,因為箱子已經(jīng)是空的了,這就是 ABA 的問題。

JDK在1.5時提供了AtomicStampedReference類也可以解決ABA的問題,此類維護了一個“版本號”Stamp,每次在比較時不止比較當前值還比較版本號,這樣就解決了 ABA 的問題。

綜合分析實例:

如一個金融系統(tǒng),當某個操作員讀取用戶的數(shù)據(jù),并在讀出的用戶數(shù)據(jù)的基礎(chǔ)上進行修改時(如更改用戶帳戶余額),如果采用悲觀鎖機制,也就意味著整個操作過程中(從操作員讀出數(shù)據(jù)、開始修改直至提交修改結(jié)果的全過程,甚至還包括操作 員中途去煮咖啡的時間),數(shù)據(jù)庫記錄始終處于加鎖狀態(tài),可見,如果面對幾百上千個并發(fā),這樣的情況將導致怎樣的后果。樂觀鎖機制在一定程度上解決了這個問題。

樂觀鎖,大多是基于數(shù)據(jù)版本( version )記錄機制實現(xiàn)。何謂數(shù)據(jù)版本?即為數(shù)據(jù)增加一個版本標識,在基于數(shù)據(jù)庫表的版本解決方案中,一般是通過為數(shù)據(jù)庫表增加一個 “version” 字段來實現(xiàn)。讀取出數(shù)據(jù)時,將此版本號一同讀出,之后更新時,對此版本號加一。同時,將提交數(shù)據(jù)的版本數(shù)據(jù)與數(shù)據(jù)庫表對應(yīng)記錄的當前版本信息進行比對,如果提交的數(shù)據(jù)版本號等于數(shù)據(jù)庫表當前版本號,則予以更新,否則認為是過期數(shù)據(jù)。

對于上面修改用戶帳戶信息的例子而言,假設(shè)數(shù)據(jù)庫中帳戶信息表中有一個 version 字段,當前值為 1 ;而當前帳戶余額字段( balance )為 $100 。

1 操作員 A 此時將其讀出( version=1 ),并從其帳戶余額中扣除 $50( $100-$50 )。

2 在操作員 A 操作的過程中,操作員B 也讀入此用戶信息( version=1 ),并從其帳戶余額中扣除 $20 ( $100-$20 )。

3 操作員 A 完成了修改工作,將 version=1 的數(shù)據(jù)連同帳戶扣除后余額( balance=$50 ),提交至數(shù)據(jù)庫更新,此時由于提交數(shù)據(jù)版本等于數(shù)據(jù)庫記錄當前版本,數(shù)據(jù)被更新,同時數(shù)據(jù)庫記錄 version 更新為 2(set version=version+1 where version=1) 。

4 操作員 B 完成了數(shù)據(jù)錄入操作,也將 version=1 的數(shù)據(jù)試圖向數(shù)據(jù)庫提交( balance=$80 ),但此時比對數(shù)據(jù)庫記錄版本時發(fā)現(xiàn),操作員 B 提交的數(shù)據(jù)版本號為 1 ,數(shù)據(jù)庫記錄當前版本也為 2 ,不滿足 “ 提交版本必須等于記錄當前版本才能執(zhí)行更新?“ 的樂觀鎖策略,因此,操作員 B 的提交被駁回。

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

悲觀鎖樂觀鎖優(yōu)缺點:

優(yōu)點:

悲觀鎖大多數(shù)情況下依靠數(shù)據(jù)庫的鎖機制實現(xiàn),以保證操作最大程度的獨占性。但隨之而來的就是數(shù)據(jù)庫性能的大量開銷,特別是對長事務(wù)而言,這樣的開銷往往無法承受。而樂觀鎖機制避免了長事務(wù)中的數(shù)據(jù)庫加鎖開銷,大大提升了大并發(fā)量下的系統(tǒng)整體性能表現(xiàn)。

缺點:

需要注意的是,樂觀鎖機制往往基于系統(tǒng)中的數(shù)據(jù)存儲邏輯,因此也具備一定的局限性,如在上例中,由于樂觀鎖機制是在我們的系統(tǒng)中實現(xiàn),來自外部系統(tǒng)的用戶余額更新操作不受我們系統(tǒng)的控制,因此可能會造成臟數(shù)據(jù)被更新到數(shù)據(jù)庫中。在系統(tǒng)設(shè)計階段,我們應(yīng)該充分考慮到這些情況出現(xiàn)的可能性,并進行相應(yīng)調(diào)整(如將樂觀鎖策略在數(shù)據(jù)庫存儲過程中實現(xiàn),對外只開放基于此存儲過程的數(shù)據(jù)更新途徑,而不是將數(shù)據(jù)庫表直接對外公開)。

使用場景

有一種說法認為,悲觀鎖由于它的操作比較重量級,不能多個線程并行執(zhí)行,而且還會有上下文切換等動作,所以悲觀鎖的性能不如樂觀鎖好,應(yīng)該盡量避免用悲觀鎖,這種說法是不正確的。因為雖然悲觀鎖確實會讓得不到鎖的線程阻塞,但是這種開銷是固定的。悲觀鎖的原始開銷確實要高于樂觀鎖,但是特點是一勞永逸,就算一直拿不到鎖,也不會對開銷造成額外的影響。反觀樂觀鎖雖然一開始的開銷比悲觀鎖小,但是如果一直拿不到鎖,或者并發(fā)量大,競爭激烈,導致不停重試,那么消耗的資源也會越來越多,甚至開銷會超過悲觀鎖。所以,同樣是悲觀鎖,在不同的場景下,效果可能完全不同。

(1) 悲觀鎖適合用于并發(fā)寫入多、臨界區(qū)代碼復雜、競爭激烈等場景,這種場景下悲觀鎖可以避免大量的無用的反復嘗試等消耗。

(2) 樂觀鎖適用于大部分是讀取,少部分是修改的場景,也適合雖然讀寫都很多,但是并發(fā)并不激烈的場景。在這些場景下,樂觀鎖不加鎖的特點能讓性能大幅提高。


請說一下悲觀鎖和樂觀鎖的區(qū)別的評論 (共 條)

分享到微博請遵守國家法律
五峰| 洛阳市| 淳安县| 合水县| 东山县| 湟中县| 方正县| 绥棱县| 新乡县| 中宁县| 台中县| 云浮市| 湟源县| 鹰潭市| 毕节市| 青海省| 股票| 夏河县| 紫金县| 孟州市| 富民县| 桂阳县| 临洮县| 长子县| 阿荣旗| 桂平市| 北流市| 依兰县| 景泰县| 上林县| 井研县| 武安市| 宽甸| 房产| 舞阳县| 社旗县| 兖州市| 钟山县| 南平市| 商南县| 馆陶县|