9.自旋鎖
a.自旋鎖的提出背景
由于在多處理器環境中某些資源的有限性,有時需要互斥訪問,這時候就需要引入鎖的概念,只有獲取了鎖的執行緒才能夠對資源進行訪問,由于多執行緒的核心是CPU的時間片,所以同一時刻只能有一個執行緒獲取到鎖,那么就面臨了一個問題,沒有獲取到的鎖的執行緒應該怎么辦?
通常有兩種方式:一種是沒有獲取到的鎖的執行緒就一直回圈等待判斷該資源是否已經釋放鎖了,這種鎖叫做自旋鎖,它不用將執行緒阻塞起來(NON-BLOCKING);還有一種處理方式就是把自己阻塞起來,等待重新調度申請,這種叫做互斥鎖.
b.什么是自旋鎖
自旋鎖的定義:當一個執行緒嘗試去獲取某一把鎖的時候,如果這個鎖此時已經被別人獲取(占用),那么此執行緒就無法獲取到這把鎖,該執行緒將會等待,間隔一段時間后會再次嘗試獲取,這種采用回圈加鎖 -> 等待的機制被稱為自旋鎖(spinlock),
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ECF418OF-1606980448928)(C:\Users\lcz\AppData\Roaming\Typora\typora-user-images\image-20201203150629681.png)]](https://img.uj5u.com/2020/12/10/203218101753331.png)
c.自旋鎖的優缺點
自旋鎖盡可能的減少執行緒的阻塞,這對于鎖的競爭不激烈,且占用鎖時間非常短的代碼塊來說性能能大幅度的提升,因為自旋的消耗會小于執行緒阻塞掛起再喚醒的操作的消耗,這些操作會導致執行緒發生兩次背景關系切換!
但是如果鎖的競爭激烈,或者持有鎖的執行緒需要長時間占用鎖執行同步塊,這時候就不適合使用自旋鎖了,因為自旋鎖在獲取鎖前一直都是占用 cpu 做無用功,占著 XX 不 XX,同時有大量執行緒在競爭一個鎖,會導致獲取鎖的時間很長,執行緒自旋的消耗大于執行緒阻塞掛起操作的消耗,其它需要 cpu 的執行緒又不能獲取到 cpu,造成 cpu 的浪費,所以這種情況下我們要關閉自旋鎖,
d.自旋鎖的實作
public class SpinLockTest {
private AtomicBoolean available = new AtomicBoolean(false);
public void lock(){
// 回圈檢測嘗試獲取鎖
while (!tryLock()){
// doSomething...
}
}
public boolean tryLock(){
// 嘗試獲取鎖,成功回傳true,失敗回傳false
return available.compareAndSet(false,true);
}
public void unLock(){
if(!available.compareAndSet(true,false)){
throw new RuntimeException("釋放鎖失敗");
}
}
}
這種簡單的自旋鎖有一個問題:無法保證多執行緒競爭的公平性,對于上面的SpinlockTest,當多個執行緒想要獲取鎖時,誰最先將available設為false誰就能最先獲得鎖,這可能會造成某些執行緒一直都未獲取到鎖造成執行緒饑餓,就像我們下課后蜂擁的跑向食堂,下班后蜂擁地擠向地鐵,通常我們會采取排隊的方式解決這樣的問題,類似地,我們把這種鎖叫排隊自旋鎖(QueuedSpinlock),計算機科學家們使用了各種方式來實作排隊自旋鎖,如TicketLock,MCSLock,CLHLock,接下來我們分別對這幾種鎖做個大致的介紹
(1)TicketLock
在計算機科學領域中,TicketLock 是一種同步機制或鎖定演算法,它是一種自旋鎖,它使用ticket 來控制執行緒執行順序,
TicketLock 是基于先進先出(FIFO) 佇列的機制,它增加了鎖的公平性,其設計原則如下:TicketLock 中有兩個 int 型別的數值,開始都是0,第一個值是佇列ticket(佇列票據), 第二個值是 出隊(票據),佇列票據是執行緒在佇列中的位置,而出隊票據是現在持有鎖的票證的佇列位置,可能有點模糊不清,簡單來說,就是佇列票據是你取票號的位置,出隊票據是你距離叫號的位置,現在應該明白一些了吧
上面這個流程的關鍵點在于,每個辦業務的人在辦完業務之后,他必須丟棄自己的號碼,叫號機才能繼續叫到下面的人,如果這個人沒有丟棄這個號碼,那么其他人只能繼續等待,
(2)CLHLock
上面說到TicketLock 是基于佇列的,那么 CLHLock 就是基于鏈表設計的,CLH的發明人是:Craig,Landin and Hagersten,用它們各自的字母開頭命名,CLH 是一種基于鏈表的可擴展,高性能,公平的自旋鎖,申請執行緒只能在本地變數上自旋,它會不斷輪詢前驅的狀態,如果發現前驅釋放了鎖就結束自旋,
(3)MCSLock
MCS Spinlock 是一種基于鏈表的可擴展、高性能、公平的自旋鎖,申請執行緒只在本地變數上自旋,直接前驅負責通知其結束自旋,從而極大地減少了不必要的處理器快取同步的次數,降低了總線和記憶體的開銷,MCS 來自于其發明人名字的首字母: John Mellor-Crummey和Michael Scott,
e.總結
自旋鎖是為了提高資源的使用頻率而出現的一種鎖,自旋鎖說的是執行緒獲取鎖的時候,如果鎖被其他執行緒持有,則當前執行緒將回圈等待,直到獲取到鎖,
自旋鎖在等待期間不會睡眠或者釋放自己的執行緒,自旋鎖不適用于長時間持有CPU的情況,這會加劇系統的負擔,為了解決這種情況,需要設定自旋周期,那么自旋周期的設定也是一門學問,
還提到了自旋鎖本身無法保證公平性,那么為了保證公平性又引出了TicketLock ,TicketLock 是采用排隊叫號的機制來實作的一種公平鎖,但是它每次讀寫操作都必須在多個處理器快取之間進行快取同步,這會導致繁重的系統總線和記憶體的流量,大大降低系統整體的性能,
所以我們又引出了CLHLock和MCSLock,CLHLock和MCSLock通過鏈表的方式避免了減少了處理器快取同步,極大的提高了性能,區別在于CLHLock是通過輪詢其前驅節點的狀態,而MCS則是查看當前節點的鎖狀態,
參考文章 看完你就明白的鎖系列之自旋鎖
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/232556.html
標籤:java
