本文主要來分析一下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的方式獲取鎖。而非公平鎖指執行緒開始可以插隊嘗試獲取鎖,如果...