日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

悲观锁和乐观锁_面试必备之乐观锁与悲观锁

發(fā)布時(shí)間:2023/12/13 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 悲观锁和乐观锁_面试必备之乐观锁与悲观锁 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

何謂悲觀鎖與樂觀鎖

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

大家可以點(diǎn)擊加群【JAVA架構(gòu)知識(shí)學(xué)習(xí)討論群】473984645,(如多你想跳槽換工作,但是技術(shù)又不夠,或者工作遇到了瓶頸,我這里有一個(gè)Java的免費(fèi)直播課程,講的是高端的知識(shí)點(diǎn),只要有1-5年的開發(fā)工作經(jīng)驗(yàn)可以加群找我要課堂鏈接。)注意:是免費(fèi)的 沒有開發(fā)經(jīng)驗(yàn)的誤入。

悲觀鎖

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

樂觀鎖

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

兩種鎖的使用場景

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

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

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

1. 版本號(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ù)庫中的version值相等時(shí)才更新,否則重試更新操作,直到更新成功。

舉一個(gè)簡單的例子:

假設(shè)數(shù)據(jù)庫中帳戶信息表中有一個(gè) version 字段,當(dāng)前值為 1 ;而當(dāng)前帳戶余額字段( balance )為 $100 。當(dāng)需要對(duì)賬戶信息表進(jìn)行更新的時(shí)候,需要首先讀取version字段。

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

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

操作員 A 完成了修改工作,提交更新之前會(huì)先看數(shù)據(jù)庫的版本和自己讀取到的版本是否一致,一致的話,就會(huì)將數(shù)據(jù)版本號(hào)加1( version=2 ),連同帳戶扣除后余額( balance=$50 ),提交至數(shù)據(jù)庫更新,此時(shí)由于提交數(shù)據(jù)版本大于數(shù)據(jù)庫記錄當(dāng)前版本,數(shù)據(jù)被更新,數(shù)據(jù)庫記錄 version 更新為 2 。

操作員 B 完成了操作,提交更新之前會(huì)先看數(shù)據(jù)庫的版本和自己讀取到的版本是否一致,但此時(shí)比對(duì)數(shù)據(jù)庫記錄版本時(shí)發(fā)現(xiàn),操作員 B 提交的數(shù)據(jù)版本號(hào)為 2 ,而自己讀取到的版本號(hào)為1 ,不滿足 “ 當(dāng)前最后更新的version與操作員第一次讀取的版本號(hào)相等 “ 的樂觀鎖策略,因此,操作員 B 的提交被駁回。

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

2. CAS算法

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

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

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

樂觀鎖的缺點(diǎn)

ABA 問題是樂觀鎖一個(gè)常見的問題

1 ABA 問題

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

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

2 循環(huán)時(shí)間長開銷大

自旋CAS(也就是不成功就一直循環(huán)執(zhí)行直到成功)如果長時(shí)間不成功,會(huì)給CPU帶來非常大的執(zhí)行開銷。 如果JVM能支持處理器提供的pause指令那么效率會(huì)有一定的提升,pause指令有兩個(gè)作用,第一它可以延遲流水線執(zhí)行指令(de-pipeline),使CPU不會(huì)消耗過多的執(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 無效。但是從 JDK 1.5開始,提供了AtomicReference類來保證引用對(duì)象之間的原子性,你可以把多個(gè)變量放在一個(gè)對(duì)象里來進(jìn)行 CAS 操作.所以我們可以使用鎖或者利用AtomicReference類把多個(gè)共享變量合并成一個(gè)共享變量來操作。

CAS與synchronized的使用情景

簡單的來說CAS適用于寫比較少的情況下(多讀場景,沖突一般較少),synchronized適用于寫比較多的情況下(多寫場景,沖突一般較多)

對(duì)于資源競爭較少(線程沖突較輕)的情況,使用synchronized同步鎖進(jìn)行線程阻塞和喚醒切換以及用戶態(tài)內(nèi)核態(tài)間的切換操作額外浪費(fèi)消耗cpu資源;而CAS基于硬件實(shí)現(xiàn),不需要進(jìn)入內(nèi)核,不需要切換線程,操作自旋幾率較少,因此可以獲得更高的性能。

對(duì)于資源競爭嚴(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)行了主要包括為了減少獲得鎖和釋放鎖帶來的性能消耗而引入的 偏向鎖 和 輕量級(jí)鎖 以及其它各種優(yōu)化之后變得在某些情況下并不是那么重了。synchronized的底層實(shí)現(xiàn)主要依靠 Lock-Free 的隊(duì)列,基本思路是 自旋后阻塞,競爭切換后繼續(xù)競爭鎖,稍微犧牲了公平性,但獲得了高吞吐量。在線程沖突較少的情況下,可以獲得和CAS類似的性能;而線程沖突嚴(yán)重的情況下,性能遠(yuǎn)高于CAS。

我這兒整理了比較全面的JAVA相關(guān)的面試資料,

需要領(lǐng)取面試資料的同學(xué),請(qǐng)加群:473984645

總結(jié)

以上是生活随笔為你收集整理的悲观锁和乐观锁_面试必备之乐观锁与悲观锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。