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

AbstractQueuedSynchronizer原始碼分析(ReentrantLock鎖的實現)

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、喚醒下一個結點