深入理解AQS原始碼解析一

2021-10-09 09:33:43 字數 4289 閱讀 2203

三、最後小結一下

一、概念

我們談到併發,就不得不談reentrantlock鎖;而談到reentrantlock鎖,不得不談一下abstractqueuedsynchronized(aqs)!類如其名,抽象的佇列式的同步器,aqs定義了一套多執行緒訪問共享資源的同步器框架,許多同步類實現都依賴於它,如常用的reentrantlock/semaphore/countdownlatch…。我們以reentrantlock作為講解切入點。

二、reentrantlock原始碼分析

reentrantlock把所有lock介面的操作都委派到乙個sync類上,該類繼承了abstractqueuedsynchronizer:

static

abstract

class

sync

extends

abstractqueuedsynchronizer

sync又有兩個子類(分為:公平鎖和非公平鎖兩類):
final

static

class

nonfairsync

extends

sync

final

static

class

fairsync

extends

sync

顯然是為了支援公平鎖和非公平鎖而定義,預設情況下為非公平鎖,設定為公平鎖可直接在建構函式中傳入true即可。

先理一下reentrant.lock()方法的呼叫過程(預設非公平鎖)

首先我們主要先講一下reentrantlock類中的lock()方法和uplock()方法都做了些什麼事情。

1、lock()方法原始碼解析

// 首先會進入reentrantlock中呼叫lock()方法,接著呼叫aqs中lock()方法(1

)、 public

void

lock()

// 對所持有資源執行緒加鎖(2

)、 final

void

lock()

// 獲取鎖資源(3

)、public

final

void

acquire

(int arg)

// 從上一步可以看出,會將當前執行緒進入等待狀態,後續將子執行緒執行完才會去釋放鎖資源(4

)、private node addwaiter

(node mode)

}enq

(node)

;// 否則進入enq()方法

return node;

}// 這個方法會解決兩個問題 1、尾節點為空 2、cas嘗試設定鎖失敗(5

)、private node enq

(final node node)

else}}

}// 從等待佇列中去獲取鎖資源(6

)、 final

boolean

acquirequeued

(final node node,

int arg)

// 嘗試獲取鎖失敗,會將當前執行緒掛起,設定狀態為signal,等待其他執行緒執行完,才會被喚醒if(

shouldparkafte***iledacquire

(p, node)

&&parkandcheckinterrupt()

) interrupted =

true;}

}finally

// 將嘗試獲取鎖失敗的執行緒掛起(7

)、 private

static

boolean

shouldparkafte***iledacquire

(node pred, node node)

while

(pred.waitstatus >0)

; pred.next = node;

}else

return

false;}

// 將當前執行緒阻塞並掛起(8

)、 private

final

boolean

parkandcheckinterrupt()

// 嘗試獲取鎖狀態(9

)、protected

final

boolean

tryacquire

(int acquires)

// 非公平鎖獲取鎖狀態(10

)、final

boolean

nonfairtryacquire

(int acquires)

}// 當出現同乙個持有鎖的執行緒,再次獲取鎖,就會進行鎖重入過程

else

if(current ==

getexclusiveownerthread()

)return

false

;// 否則獲取鎖狀態失敗,符合fifo規則

}

通過上面分析完lock()方法以後,我們來看一下uplock()方法中原始碼解析

2、uplock()方法原始碼解析

// 1、首先uplock會去呼叫aqs裡面的release()方法

public

void

unlock()

// 2、aqs中release()方法

public

final

boolean

release

(int arg)

return

false;}

// 3、tryrelease()方法在reentrantlock類中呼叫

protected

final

boolean

tryrelease

(int releases)

setstate

(c);

// 重新設定state狀態,在這裡也有可能發生鎖重入

return free;

}// 4、呼叫aqs中的unparksuccessor()方法

private

void

unparksuccessor

(node node)

if(s != null)

// 判斷當前節點的下乙個節點是否為空

locksupport.

unpark

(s.thread)

;// 若不為空,則喚醒該執行緒

}

在這裡通過對uplock()原始碼分析可知:首先會判斷當前執行緒鎖持有者是否被釋放,若是state = 0,表明鎖被釋放且獲取頭節點,判斷頭節點是否為空且等待狀態不為cacelled,則會將頭節點中的後續節點喚醒,否則繼續會讓後續節點掛起狀態,等待被喚醒。

三、最後小結一下

1、reentrantlock鎖中最主要的就是lock()方法和uplock()方法,通過對這兩個方法原始碼分析,你可以發現,在併發過程中它是怎麼去加鎖和釋放鎖的過程,最主要的就是為什麼加鎖過程還需要用到cas方式去獲取鎖,若是不使用cas獲取鎖,有會引發什麼問題,有沒有更好的解決方案?這個你們可以深入考慮一下。

2、通過原始碼分析中,我們接觸到了公平鎖與非公平鎖之間獲取鎖過程中有什麼不同?各自都有什麼優勢,哪這裡又引入了乙個問題,在你們的理解中什麼是公平鎖和非公平鎖?

3、其實,從reentrantlock切入點分析過程中,並不是特別難,包括cas獲取鎖和加鎖的過程以及引入公平鎖與非公平鎖之間的實現方式,各自的優勢體現在哪方面;主要我們還是要去理解去掌握它的有關實現方式,我們可以想一下有沒比這種方式更好的實現方案。

結語:後續會繼續講aqs中的countdownlatch原始碼解析~

AQS深入理解

獨佔鎖的釋放 release方法 獨佔鎖的獲取與釋放總結 能響應中斷以及超時等待 共享鎖aqs實現的三個功能 對同步狀態的管理 對阻塞執行緒進行排隊 等待通知等底層功能的實現 aqs通過頭尾指標管理同步佇列的,獲取鎖失敗的執行緒入隊,釋放鎖對同步佇列中的執行緒進行通知 當共享資源被某個執行緒占有,其...

Okhttp深入理解及原始碼解析

okhttpclient okhttpclient new okhttpclient 第一行 request request new request.builder url url build 第二行 call call okhttpclient.newcall request 第三行 1 建立 o...

AQS原始碼解析

公平鎖 fairsync 核心方法 public final void acquire int arg tryacquire arg 方法 protected final boolean tryacquire int acquires 重入鎖 state 1 else if current gete...