1. 程式人生 > 實用技巧 >ReentratLock原始碼分析,AQS原理解析

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()

未完待續...