1. 程式人生 > >AbstractQueuedSynchronizer源碼分析(ReentrantLock鎖的實現)

AbstractQueuedSynchronizer源碼分析(ReentrantLock鎖的實現)

所有 釋放 true alt 之前 非公平鎖 通過 而且 16px

1. 前言

Java中好多地方用到AbstractQueuedSynchronizer(PS:簡稱AQS),比如ReentrantLock、線程池,這部分在面試的時候也經常被問到,今天以ReentrantLock為例,通過源碼來加深對AQS的理解

技術分享圖片

2. lock

通常,我們的用法是這樣的:

技術分享圖片

那麽lock(),unlock()到底做了什麽,我們並不知曉,接下來一步一步揭開她的神秘面紗

2.1. lock

技術分享圖片

可以看到,ReentrantLock默認是NonfairSync(非公平鎖)

2.2. NonfairSync

技術分享圖片

技術分享圖片

技術分享圖片

可以看到,首先判斷同步狀態是否是0,如果是0則臨界資源沒有被占用,並且將狀態設為1,當前線程獲得這個資源,可以訪問;否則,調用acquire(1)方法嘗試獲取資源的訪問控制權。

2.3. tryAcquire

技術分享圖片

技術分享圖片

技術分享圖片

嘗試再獲取一次

如果當前同步狀態是0,則跟之前相同,將狀態置為1,當前線程獲得訪問權,返回true

如果當前資源的所有者線程就是當前線程,則狀態加1,當前線程仍然獲得訪問權,返回true

如果以上情況都不是(PS:資源被別的線程占用著),則返回false

2.4. acquireQueued

如果上一步tryAcquire方法返回false,則繼續調用acquireQueued方法將線程添加到隊列中(PS:鏈表的尾部)

在添加到鏈表之前,先封裝成Node對象

技術分享圖片

技術分享圖片

技術分享圖片

將當前線程構造成Node對象,節點的模式是排它的,然後將其加到鏈表的尾部(或是說叫隊列),最後將該結點返回

這裏有兩個特殊的節點:等待隊列的頭結點 和 等待隊列的尾結點

技術分享圖片

接下來,將剛才構造的節點加到隊列中

技術分享圖片

技術分享圖片

如果當前節點的前驅節點是頭結點(head),並且當前節點再次嘗試獲取資源(tryAcquire方法),恰好成功了,於是皆大歡喜

如果當前節點的前驅節點既不是head,而且當前節點也沒有搶占到資源,則循環直到前驅節點的狀態變成SIGNAL,則掛起當前線程

技術分享圖片

3. unlock

技術分享圖片

技術分享圖片

技術分享圖片

如果資源的所有者線程不是當前線程的話,則拋出異常

如果當前資源同步狀態減1恰好是0,則成功釋放,同步狀態置為0,資源所有者線程置為null,喚醒head的後繼節點

技術分享圖片

4. 小結

加鎖

1、默認非公平鎖(PS:當前線程不是直接加到等待隊列中,而是先嘗試獲取一次,如果不成功則加到隊列中,在加的時候還會嘗試一次)

2、臨街資源有一個同步狀態,0表示當前沒有線程占用,則可以直接可以獲取到資源(加鎖成功)

3、如果資源所有者線程就是當前線程,則狀態加1,仍然可以獲得鎖

3、加鎖成功以後,將同步狀態置為1,資源所有者線程置為當前線程

4、不成功,則封裝成Node加入到等待隊列(鏈表)中,此時,還會像前面一樣再嘗試(搶占)一次

5、搶占不成功,加到等待隊列中,線程掛起

解鎖

1、資源所有者線程不是當前線程,則拋異常

2、喚醒下一個結點

AbstractQueuedSynchronizer源碼分析(ReentrantLock鎖的實現)