JDK鎖的基礎 AQS實現原理(三)

2021-08-18 13:44:50 字數 2624 閱讀 1359

本文主要來分析一下aqs共享模式鎖的獲取和釋放,aqs其實只是乙個框架,它主要提供了乙個int型別的state欄位,子類繼承時用於儲存子類的狀態,並且提供了乙個等待佇列以及維護等待佇列的方法。至於如何使用這個狀態值和等待佇列,就需要子類根據自己的需求來實現了。

以semaphore類為例,semaphore允許多個執行緒同時獲得訊號量先來看一下semaphore的介面:

//semaphore

public

void

acquire() throws interruptedexception

同樣的,sync是乙個定義在semaphore中的aqs的抽象子類,在semaphore類中有兩種實現,乙個是公平的,乙個是非公平的。轉到aqs中的acquiresharedinterruptibly方法,

//abstractqueuedsynchornizer

public

final

void

acquiresharedinterruptibly(int arg)

throws interruptedexception

private

void

doacquiresharedinterruptibly(int arg)

throws interruptedexception

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

parkandcheckinterrupt())

throw

new interruptedexception();

}} finally

}

來看一下setheadandpropagate方法,這個方法和sethead不同的地方在於它不僅設定了等待佇列的頭節點,並且檢查其後繼節點是否可能是共享模式節點,如果是,而且傳入的propagate大於0或者頭節點設定了propagate狀態,那麼需要呼叫doreleaseshared方法來喚醒後繼節點。setheadandpropagate方法的處理過程比較保守,可能會導致很多不必要的喚醒。

private

void

setheadandpropagate(node node, int propagate)

}

可以看到setheadandpropagate方法的原則是寧濫勿缺,反正doreleaseshared方法會繼續後來的處理:

private

void

doreleaseshared()

//如果頭節點的狀態為0,說明後繼節點還沒有被阻塞,不需要立即喚醒

//把頭節點的狀態設定成propagate,下次呼叫setheadandpropagate的時候前任頭節點的狀態就會是propagate,就會繼續呼叫doreleaseshared方法把喚醒「傳播」下去

else

if (ws == 0 &&

!compareandsetwaitstatus(h, 0, node.propagate))

continue; // loop on failed cas

}//如果頭節點被修改了那麼繼續迴圈下去

if (h == head) // loop if head changed

break;

}}

根據自己的思考總結一下,不保證正確性:

aqs的等待佇列的頭節點在初始化的時候是個啞節點,其它時候代表已經獲取鎖的節點(獨佔模式)或者獲取了permit的節點(共享模式),設定了頭節點的執行緒已經可以執行臨界區**了。也就是說,在共享模式下,獲得了permit的執行緒代表的節點可能被其它節點擠出等待佇列。總之,等待佇列從第二個節點開始才是正在等待的執行緒。

aqs的等待佇列的節點類node只有在其後繼節點被阻塞的情況下才會是signal狀態,所以signal狀態代表其後繼節點正在阻塞中。

aqs等待佇列節點的propagate狀態代表喚醒的行為需要傳播下去,當頭節點的後繼節點並未處於阻塞狀態時(可能是剛呼叫addwaiter方法新增到佇列中還未來得及阻塞),就給頭節點設定這個標記,表示下次呼叫setheadandpropagate函式時會把這個喚醒行為傳遞下去。

設定propagate狀態的意義主要在於,每次釋放permit都會呼叫doreleaseshared函式,而該函式每次只喚醒等待佇列的第乙個等待節點。所以在本次歸還的permit足夠多的情況下,如果僅僅依靠釋放鎖之後的一次doreleaseshared函式呼叫,可能會導致明明有permit但是有些執行緒仍然阻塞的情況。所以在每個執行緒獲取到permit之後,會根據剩餘的permit來決定是否把喚醒傳播下去。但不保證被喚醒的執行緒一定能獲得permit。

共享模式下會導致很多次不必要的喚醒。

JDK鎖的基礎 AQS實現原理(二)

上文介紹了aqs的一些基礎知識,包括clh鎖的原理和aqs的一些資料結構,這篇文章中我們來分析一下aqs的方法。aqs是乙個抽象類,定義了幾個模板方法交給子類去實現,分別是 protected boolean tryacquire int arg protected boolean tryrelea...

AQS共享鎖的實現原理

前面的文章lock的實現中分析了aqs獨佔鎖的實現原理,那麼接下來就分析下aqs是如何實現共享鎖的。共享鎖的介紹 共享鎖 同一時刻有多個執行緒能夠獲取到同步狀態。那麼它是如何做到讓多個執行緒獲取到同步狀態呢?來看一下獲取共享鎖的過程 1.執行緒呼叫aqs的acquireshared 申請獲取鎖 可有...

基於AQS的獨佔鎖實現邏輯

獨佔鎖的正常使用方式,先從加鎖邏輯開始。lock lock newreentrantlock public void test finally reentrantlock分公平鎖和非公平鎖,公平鎖指搶鎖的執行緒進來先入佇列排隊,按照fifo的方式獲取鎖。而非公平鎖指執行緒開始可以插隊嘗試獲取鎖,如果...