ReentratLock原始碼分析,AQS原理解析
先畫個大致的假類圖
主要的類都在這裡,核心就是ReentrantLock的內部類 Sync,
FairSync NonfairSync 是Sync的公平鎖 非公平鎖的實現
Sync繼承於AbstractQueueSynchronizer(AQS) 核心功能也都在這 先來分析AQS
AQS的核心思想就是: 一個佇列(實質是一個連結串列結構) + 一個狀態 / a Queue and a state
大致思路 :
state代表當前鎖的狀態 預設為0,當該鎖被某個執行緒持有則state 為1,如果是重入鎖 每重入一次state+1
當執行緒嘗試獲取鎖失敗就將執行緒自己封裝成Node節點加入到佇列,然後掛起等待被喚醒,再去競爭鎖
完結 !
原始碼分析:
競爭鎖的佇列該有的樣子
AQS的幾個核心屬性 : head, tail, state,exclusiveOwnerThread
head 表示佇列的頭節點 (頭節點不屬於阻塞佇列!頭節點不代表實際的執行緒)
state 表示當前鎖的狀態 0 代表無人佔有 1 代表被執行緒佔用,每+1 代表被重入一次
tail 表示佇列的尾節點
AQS繼承於AbstractOwnableSynchronizer
繼承了重要屬性 exclusiveOwnerThread 表示當前佔有鎖的執行緒
AQS內部類 Node
幾個重要的屬性:
next 後繼節點,
perv 先驅節點,
thread 該節點代表的執行緒,
waitStatus 表示後繼節點是否需要喚醒 (由後繼節點控制,預設是0 不需要喚醒,當後繼節點需要被前置節點喚醒則將0設定為-1,如果前置節點主動放棄競爭鎖 則將waitStatus > 0)
EXCLUSIVE 標識節點會獨佔鎖
SHARED 標識節點會共享鎖
從客戶端呼叫入手分析
加鎖過程
public static void main(String[] args) {
Lock lock = new ReentrantLock();
lock.lock();
try {
//do something
}finally {
lock.unlock();
}
}
1.初始化 無參預設是非公平鎖
2.lock.lock(); 非公平鎖的實現
先試探CAS去改鎖的狀態,如果成功則加鎖成功! 將持有鎖的執行緒設定為自己 over!
如果CAS失敗則 執行acquire(1), 引數傳1
3. acquire 決定是否要將自己中斷
if(嘗試獲取鎖失敗 && 將當前執行緒封裝好了加入到阻塞佇列都處理好了){
將自己中斷();
}
4. 重點:tryAcquire(1) 嘗試去獲取鎖 傳的引數是1
非公平鎖的tryAcquire()實現,
先判斷state是否為0 (當前鎖是否沒被持有)
if (state == 0){
if (嘗試CAS直接去改變狀態 因為此時可能會有多個執行緒同時發現了state是0 都想去改變狀態){
如果CAS成功了就將持有鎖的執行緒設定為自己 over!
}
}else if (當前執行緒就是當前持有鎖的執行緒,說明這是鎖重入 ){
直接state+1
}
嘗試獲取鎖失敗了 return false;
5. boolean acquireQueued() 中斷當前執行緒是否成功 , 傳入封裝好的節點 和引數1
try {
for(;;){
獲取當前節點的前直接點 p
if (如果p是head節點 並且 再次嘗試獲取鎖成功了){
就把當前節點設定為head節點
下個節點置空
return 中斷當前執行緒失敗
}
if ( 再次搶佔鎖失敗後是否需要掛起 && 檢查並掛起執行緒){
中斷成功
}
}
} finally {
//failed == true的情況 再次嘗試獲取鎖拋異常導致
if (如果中斷失敗了){
解散節點 不玩了
}
}
6. addWaiter() 嘗試加入到阻塞佇列 傳入的mode標識當前節點是想 獨佔鎖 還是想共享鎖
將當前執行緒封裝成一個Node
獲取到尾節點 pred
if (如果尾節點不為null){
將perd作為當前節點的前置節點
if (CAS將當前節點設定為佇列的尾節點){
return 當前節點;
}
}
說明尾節點是null,就說明沒有head節點,
for迴圈{
判斷尾節點是否為空,如果為空則CAS 將自己設定為head節點 兼 尾節點,
如果中途判斷髮現尾節點不為空了(可能是被別人搶先佔了頭節點了),就默默的加入到尾節點後面
}
解鎖過程 unlock()
未完待續...