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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

并发编程中常见的锁机制:乐观锁、悲观锁、CAS、自旋锁、互斥锁、读写锁

發布時間:2024/4/11 编程问答 48 豆豆
生活随笔 收集整理的這篇文章主要介紹了 并发编程中常见的锁机制:乐观锁、悲观锁、CAS、自旋锁、互斥锁、读写锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 樂觀鎖 VS 悲觀鎖
    • 悲觀鎖
    • 樂觀鎖
  • CAS
    • CAS機制
    • ABA問題
    • CAS的優缺點
  • 互斥鎖 VS 自旋鎖
    • 互斥鎖
    • 自旋鎖
    • 對比及應用場景
  • 讀寫鎖
    • 實現方式
    • 讀寫鎖 VS 互斥鎖


樂觀鎖 VS 悲觀鎖

樂觀鎖和悲觀鎖故名思意,它們的區別就是做事的心態不同

悲觀鎖

悲觀鎖做事比較悲觀,它始終認為共享資源在我們使用的時候會被其他線程修改,容易導致線程安全的問題,因此在訪問共享數據之前就要先加鎖,阻塞其他線程的訪問

常見的例子就是數據庫中的行鎖、表鎖、讀鎖、寫鎖等


樂觀鎖

樂觀鎖則于悲觀鎖相反,它則比較樂觀。它始終認為多線程同時修改共享資源的概率較低,所以先不管三七二十一,改了再說。

樂觀鎖會直接對共享資源進行修改,但是在更新修改結果之前它會驗證這段時間有沒有其他線程對資源進行修改,如果沒有則提交更新,如果有的話則放棄本次操作。

由于樂觀鎖全程沒有進行加鎖,所以它也被稱為無鎖編程通常以CAS操作+版本號機制實現


CAS

CAS機制

CAS是英文單詞Compare And Swap的縮寫,也就是比較和替換,這也正是它的核心。
CAS機制中用到了三個基本操作數,內存地址V,舊預期值A,新預期值B

當我們需要對一個變量進行修改時,會對內存地址V和舊預期值進行比較,如果兩者相同,則將舊預期值A替換成新預期值B。而如果不同,則將V中的值作為舊預期值,繼續重復以上操作,即自旋

下面分別舉出成功和失敗的例子
此時內存地址中存儲的值為9,線程1的舊預期值為9,新預期值為10,即我們要對里面的值進行一個加一操作

此時舊預期值與V相同,將B與V交換

此時修改成功。

接著看看修改失敗的情況

此時V中的值為9,線程1中的舊預期值為9,想將V中的值修改為10

當我們正要開始修改時,突然一個線程搶先更新數據,此時V的值變為了14

由于此時A的值與V不同,我們就需要重新獲取V中的值,并計算出新的預期值

此時兩者相同,完成替換,V=15

從上面可以看出,CAS是樂觀鎖,它樂觀地認為程序中的并發情況不那么嚴重,所以讓線程不斷去嘗試更新。


ABA問題

所謂的ABA問題,就是將一個變量從A變為了B,再從B變為了A

假設我正在銀行中提款,此時我的賬戶中有1000元,我想從中取出500元,但是由于忽然的網絡波動,此時這個操作被重復了兩次,于是如下圖

此時我們只能執行第一個扣費,由于執行完后A != V,所以第二個線程即不斷自旋比較

此時正好舍友還了你幾年前借的500塊錢,你的金額又重新變為了1000

這時線程2又給你扣了500,于是你取出了500塊錢,卻意外的扣了1000

那么這個問題如何解決呢?我們可以引入版本號機制只有版本號相同的時候才能進行替換操作

當舍友給你轉賬的時候,由于數值發生了變化,版本號也得到了修改

此時雖然我們A和V中的數值相同,但是版本號不同,所以無法進行交換


CAS的優缺點

優點

  • 在并發量少或者對變量修改操作少的時候,效率會比傳統的加鎖高,因為不涉及用戶態和內核態的切換。

缺點

  • 自旋進行比較和替換,當并發量大的時候可能會因為變量一直更新而無法比較成功,而不斷地進行自旋,導致CPU壓力過大
  • CAS只能保證一個變量的原子性,并不能保證整個代碼塊的原子性,所以在處理多個變量的原子性更新的時候還是得加鎖。
  • 上述的ABA問題,可以通過引入版本號解決

互斥鎖 VS 自旋鎖

互斥鎖和自旋鎖是最底層的兩種鎖,大部分的高級鎖都是基于它們實現的,下面就來講講它們的區別

互斥鎖

互斥鎖是一種睡眠鎖,即當一個線程占據了鎖之后,其他加鎖失敗的線程就會進行睡眠

例如我們有A、B兩個線程一同爭搶互斥鎖,當線程A成功搶到了互斥鎖時,該鎖就被他獨占,在它釋放鎖之前,B的加鎖操作就會失敗,并且此時線程B將CPU讓給其他線程,而自己則被阻塞。

對于互斥鎖加鎖失敗后進入阻塞的現象,由操作系統的內核實現,如下圖

  • 當加鎖失敗時,內核會將線程置為睡眠狀態,并將CPU切換給其他線程運行。此時從用戶態切換至內核態
  • 當鎖被釋放時,內核將線程至為就緒狀態,然后在合適的時候喚醒線程獲取鎖,繼續執行業務。此時從內核態切換至用戶態

所以當互斥鎖加鎖失敗的時候,就伴隨著兩次上下文切換的開銷,而如果我們鎖定的時間較短,可能上下文切換的時間會比鎖定的時間還要長。

雖然互斥鎖的使用難度較低,但是考慮到上下文切換的開銷,在某些情況下我們還是會優先考慮自旋鎖。


自旋鎖

自旋鎖是基于CAS實現的,它在用戶態完成了加鎖和解鎖的操作,不會主動進行上下文的切換,因此它的開銷相比于互斥鎖也會少一些。

任何嘗試獲取該鎖的線程都將一直進行嘗試(即自旋),直到獲得該鎖,并且同一時間內只能由一個線程能夠獲得自旋鎖。

自旋鎖的本質其實就是對內存中一個整數的CAS操作,加鎖包含以下步驟

  • 查看整數的值,如果為0則說明鎖空閑,則執行第二步,如果為1則說明鎖忙碌,執行第三步
  • 將整數的值設為1,當前線程進入臨界區中
  • 繼續自旋檢查(回到第一步),直到整數的值為0
  • 從上面可以看出,對于獲取自旋鎖失敗的線程會一直處于忙等待的情況,不斷自旋直至獲取鎖資源,這也就要求我們必須要盡快釋放鎖,否則會占用大量的CPU資源


    對比及應用場景

    由于自旋鎖和互斥鎖的失敗策略不同,自旋鎖采用忙等待的策略,而互斥鎖采用線程切換的策略,由于策略不同,它們的應用場景也不同。

    由于自旋鎖不需要進行線程切換,所以它完全在用戶態下實現,加鎖開銷低,但是由于其采用忙等待的策略,對于短期加鎖來說沒問題,但是長期鎖定的時候就會導致CPU資源的大量消耗。并且由于它不會睡眠,所以它可以應用于中斷處理程序中。

    互斥鎖采用線程切換的策略,當切換到別的線程的時候,原線程就會進入睡眠(阻塞)狀態,所以如果對睡眠有要求的情況可以考慮使用互斥鎖。并且由于睡眠不會占用CPU資源,在長期加鎖中它比起自旋鎖有極大的優勢

    具體的應用場景如下表格所示

    需求加鎖方式
    低開銷加鎖自旋鎖
    短期鎖定自旋鎖
    長期鎖定互斥鎖
    中斷上下文中加鎖自旋鎖
    持有鎖需要睡眠互斥鎖

    讀寫鎖

    讀寫鎖用于明確區分讀操作和寫操作的場景

    其核心在于寫獨占,讀共享

    • 讀鎖是一個共享鎖,當沒有線程持有寫鎖的時候,讀鎖就可以被多個線程并發的持有,大大的提高了共享資源的訪問效率。由于讀鎖只具備讀權限,因此不存在線程安全問題。
    • 寫鎖是一個獨占鎖(排他鎖),當有任何一個線程持有寫鎖的時候,其余線程獲取讀鎖和寫鎖的操作都會被阻塞

    如下圖

    讀鎖寫鎖
    讀鎖兼容不兼容
    寫鎖不兼容不兼容

    實現方式

    根據實現方式的不同,讀寫鎖又分為讀者優先、寫者優先、讀寫公平

    讀者優先

    讀者優先期望的是讀鎖能夠被更多的線程持有,以提高讀線程的并發性。

    為了做到這一點,它的規則如下:即使有線程申請了寫鎖,但是只要還有讀者在讀取內容,就允許其他的讀線程繼續申請讀鎖,而將申請寫鎖的進程阻塞,直到沒有讀線程在讀時,才允許該線程寫

    流程如下圖

    寫者優先

    而寫者優先則是優先服務于寫進程

    假設此時有讀線程已經持有讀鎖,正在讀,而另一寫線程申請了寫鎖,寫線程被阻塞。為了能夠保證寫者優先,此時后來的讀線程獲取讀鎖時則會被阻塞。而當先前的讀線程釋放讀鎖時,寫線程則進行寫操作,直到寫線程寫完之前,其他的線程都會被阻塞。

    流程如下圖

    讀寫公平

    從上面兩個規則可以看出,讀寫優先都會導致另一方饑餓

    • 讀者優先時,對于讀進程并發性高,但是如果一直有都進程獲取讀鎖,就會導致寫進程永遠獲取不到寫鎖,此時就會導致寫進程饑餓。
    • 寫者優先時,雖然可以保證寫進程不會餓死,但是如果一直有寫進程獲取寫鎖,導致讀進程永遠獲取不到讀鎖,此時就會導致讀進程饑餓。

    既然偏袒哪一方都會導致另一方被餓死,所以我們可以搞一個讀寫公平的規則

    實現方式:用隊列把獲取鎖的線程排隊,不管是寫線程還是讀線程都按照先進先出的原則加鎖,這也讀線程仍然可以并發,也不會出現饑餓的情況。


    讀寫鎖 VS 互斥鎖

    性能方面來說,讀寫鎖的效率并不比互斥鎖高。讀鎖加鎖的開銷并不比互斥鎖小,因為它要實時維護當前讀者的數量,在臨界區很小,鎖競爭不激烈的情況下,互斥鎖的效率往往更快

    雖然讀寫鎖在速度上可能不如互斥鎖,但是并發性好,對于并發要求高的地方,應該優先考慮讀寫鎖。

    總結

    以上是生活随笔為你收集整理的并发编程中常见的锁机制:乐观锁、悲观锁、CAS、自旋锁、互斥锁、读写锁的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。