讀AQS原始碼 關於同步佇列與鎖的公平性

2021-10-11 09:17:12 字數 1341 閱讀 7971

先上部分原始碼:

public final void acquire(int arg)

final boolean acquirequeued(final node node, int arg)

if(shouldparkafte***iledacquire(p, node)

&& parkandcheckinterrupt(

)) interrupted =

true;}

} finally

}private volatile int state;

aqs維護乙個fifo佇列,用於儲存獲取同步狀態失敗的執行緒組成的節點,先進行獲取動作的排在隊前。既然是fifo佇列,aqs是如何實現非公平鎖的?

注意到:同步佇列維持的是喚醒順序,不是獲取鎖的順序,當然這二者在某些情況下可能等價,比如要獲取鎖的程序均已進入同步佇列,那麼喚醒順序就是獲得鎖的順序。

但是,對於其他情況,因為lock呼叫的是acquire模板方法,原始碼如上,看到在新執行緒每次獲取鎖時,先嘗試獲取鎖,只有當失敗了才會進入同步佇列,這個試的過程就會導致非公平鎖的產生,假如有如下場景:

1.a執行緒持有鎖並作為同步佇列的head存在,其後依次為bcd三個節點

2.e執行緒準備獲取鎖

3.a執行緒釋放鎖,同步狀態加1,並會喚醒同步佇列的後繼節點中的執行緒,也就是b中的執行緒。

4.bcd自加入同步佇列後就一直在進行自旋(檢查前驅是否為head並嘗試獲取鎖否則找到安全點並休息),此時從b的角度看,無非又是一次重複多次的自旋。

5.在b自旋檢查條件時(前驅是否為head並嘗試獲取鎖),e正好來了,e一上來就直接是

if (!tryacquire(arg) &&acquirequeued(addwaiter(node.exclusive), arg))

此時b執行if (p == head && tryacquire(arg))可能滿足,e執行tryacquire(arg)可能滿足,就會出現爭用。

6.如果e先執行成功(將volatile型別的state減1,那b就會因快取一致性而無效其快取而重新從記憶體中讀取,發現state已滿,不可獲得),b失敗,則產生非公平鎖(b在同步佇列等了好長時間卻沒有獲得鎖)

7.要實現公平鎖,就要在tryacquire(arg)中做文章,讓新來的執行緒別瞎試,試之前先看同步佇列有沒有節點,沒有再去嘗試獲取鎖(bcd:沒看見a後面我們幾個排著隊呢,你一新來的別瞎試,到後面排隊)

AQS 同步佇列共享模式

首先來看看acquireshared public final void acquireshared int arg tryacquireshared 方法同樣由自定義同步器實現,用來給state原子的加一些操作。如果tryacquireshared 0說明當前執行緒嘗試獲取資源失敗需要進入同步佇列...

同步器AQS中的同步佇列與等待佇列

在單純地使用鎖,比如reentrantlock的時候,這個鎖元件內部有乙個繼承同步器aqs的類,實現了其抽象方法,加鎖 釋放鎖也只是涉及到aqs中的同步佇列而已,那麼等待佇列又是什麼呢?當使用condition的時候,等待佇列的概念就出來了。condition的獲取一般都要與乙個鎖lock相關,乙個...

《看透springmvc原始碼分析與實踐》讀書筆記一

解決速度問題的核心是解決海量資料操作問題和高併發問題。複雜的架構就是從這兩個問題演變出來的。1.快取和頁面靜態化 將從資料庫獲取的資料暫時儲存起來,在下次使用的時候無需重新到資料庫中獲取,這樣可以大大降低資料庫的壓力。快取可以通過程式直接儲存到記憶體中 使用map,尤其是使用concurrentha...