ReentrantLock 非公平鎖實(shí)現(xiàn)原理詳解
正文
在線程間通信方式2一節(jié)中,我們了解了Lock,Condition和ReentrantLock,學(xué)習(xí)了簡(jiǎn)單使用Condition和RentrantLock完成線程間通信,從文章中我們了解到ReentrantLock是Lock接口的一個(gè)最常用的實(shí)現(xiàn)類,ReentrantLock是獨(dú)占鎖,獨(dú)占鎖的場(chǎng)景下又支持公平鎖和非公平鎖,那么在源碼實(shí)現(xiàn)中,ReentrantLock繼承關(guān)系,實(shí)現(xiàn)結(jié)構(gòu)又是怎樣的呢?
ReentrantLock繼承關(guān)系及關(guān)聯(lián)類
在多線程與鎖中,我們了解到ReentrantLock是支持公平鎖和非公平鎖的,對(duì)應(yīng)的構(gòu)造函數(shù)源碼如下:
// ReentrantLock.java
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
可以看到如果是公平鎖則創(chuàng)建FairSync對(duì)象,如果是非公平鎖則創(chuàng)建NonfairSync,再結(jié)合Lock接口核心的lock,tryLock,unlock函數(shù)源碼(Lock接口在線程間通信方式2中有介紹)可以看出,在ReentrantLock中,使用FairSync或NonfairSync代理了鎖狀態(tài)管理,lock,tryLock和unlock實(shí)現(xiàn)源碼如下:
// ReentrantLock.java
public void lock() {
sync.lock();
}
?
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
?
public void unlock() {
sync.release(1);
}
由此,進(jìn)一步梳理ReentrantLock實(shí)現(xiàn),可以得到下圖:

上圖中一些生僻類及其作用見下表:
| 類名 | 說(shuō)明 | 備注 |
|---|---|---|
| FairSync | ReentrantLock中公平鎖的實(shí)現(xiàn)類 | / |
| NonfairSync | ReentrantLock中非公平鎖的實(shí)現(xiàn)類 | / |
| Sync | 公平鎖和非公平鎖實(shí)現(xiàn)類的共同父類,使用AbstractQueuedSynchronizer狀態(tài)記錄鎖的持有數(shù)據(jù) | / |
| AbstractQueuedSynchronizer | AbstractQueuedSynchronizer簡(jiǎn)稱AQS,一個(gè)用于實(shí)現(xiàn)阻塞鎖和同步器的工具類,其內(nèi)部維護(hù)一個(gè)先進(jìn)先出的等待隊(duì)列(真實(shí)數(shù)據(jù)結(jié)構(gòu)是雙向鏈表),依賴一個(gè)int型的數(shù)據(jù)管理同步狀態(tài) | / |
| AbstractOwnableSynchronizer | AQS的父類,定義了一個(gè)線程獨(dú)占的同步器,用于實(shí)現(xiàn)創(chuàng)建鎖和鎖占有標(biāo)記的基礎(chǔ)類,其內(nèi)部使用exclusiveOwnerThread記錄當(dāng)前占用鎖的線程 | / |
| Serializable | 序列化接口 | / |
ReentrantLock.lock流程分析
跟蹤ReentrantLock.lock調(diào)用流程,可以得到下面的時(shí)序圖(以非公平鎖為例分析):

上圖中描述了ReentrantLock中l(wèi)ock在資源空和資源被占用的情況下的執(zhí)行流程,接下來(lái)我們來(lái)看下其內(nèi)部的細(xì)節(jié)實(shí)現(xiàn)。
ReentrantLock.lock獲取鎖成功
NonfairSynck類中的lock代碼如下所示:
// NonfairSync.java
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
可以看出,我們?cè)谡{(diào)用了ReentrantLock.lock后,NonfairSync會(huì)首先嘗試通過(guò)CAS將資源占用狀態(tài)置為1(compareAndSetState,默認(rèn)值0,期望值1),如果執(zhí)行成功,說(shuō)明當(dāng)前獲取共享資源成功,將當(dāng)前線程設(shè)置為獨(dú)占鎖持有者,當(dāng)前線程繼續(xù)執(zhí)行。
compareAndSetState
compareAndSetState操作的是AQS中聲明的一個(gè)int型的值,如果其值為0,表示當(dāng)前鎖空閑,如果有線程到來(lái)可以占用鎖,如果值大于1,表示當(dāng)前鎖被占用,為保證多線程對(duì)該值的操作實(shí)時(shí)可見,使用volatile修飾該變量,相關(guān)代碼如下:
// AbstractQueuedSynchronizer.java
private volatile int state;
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
setExclusiveOwnerThread
setExclusiveOwnerThread最終是將當(dāng)前線程設(shè)置到AbstractOwnableSynchronizer中定義的exclusiveOwnerThread中,代碼如下:
// AbstractOwnableSynchronizer.java
private transient Thread exclusiveOwnerThread;
?
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
transient關(guān)鍵字:
transient關(guān)鍵字的主要作用是讓某些被transient關(guān)鍵字修飾的變量不被序列化,如果對(duì)transient修飾的變量執(zhí)行了序列化,則該變量會(huì)重新執(zhí)行默認(rèn)初始化,反序列化得到的對(duì)象是null
ReentrantLock.lock獲取鎖失敗
前文中可以看到如果compareAndSetState執(zhí)行返回false的話,就說(shuō)明當(dāng)前共享資源被占用,隨后走else邏輯,執(zhí)行acquire(1),其內(nèi)容如下:
// NonfairSync.java
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
?
// AbstractQueuedSynchronizer.java
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
其內(nèi)部主要由兩塊組成,tryAcquire和acquireQueued,其中tryAcquire再次嘗試獲取鎖,如果獲取成功,則流程結(jié)束,當(dāng)前線程正常繼續(xù)執(zhí)行,如果獲取失敗,則執(zhí)行acquireQueued方法(由于使用且操作符連接tryAcquire和acquireQueued,所以只有tryAcquire返回false的時(shí)候,才會(huì)執(zhí)行acquireQueued方法),該方法接受addWaiter返回的Node參數(shù),下面來(lái)詳細(xì)看下兩個(gè)函數(shù)的具體實(shí)現(xiàn)。
tryAcquire
tryAcquire在NonfairSync中的實(shí)現(xiàn)如下,其最終調(diào)用到的是Sync類的nonfairTryAcquire方法:
// NonfairSync.java
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
// Sync.java
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
// 獲取當(dāng)前同步資源狀態(tài)
int c = getState();
// 同步資源空閑
if (c == 0) {
// 占用同步資源更新資源狀態(tài)
if (compareAndSetState(0, acquires)) {
// 設(shè)置當(dāng)前線程為資源對(duì)應(yīng)的獨(dú)占鎖持有者
setExclusiveOwnerThread(current);
return true;
}
}
// 如果當(dāng)前線程就是持有鎖線程,更新鎖狀態(tài),直接持有鎖
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
可以看到在tryAcquire中實(shí)際上也是嘗試獲取鎖的過(guò)程,首先檢查當(dāng)前當(dāng)前同步資源狀態(tài),如果不可獲取,則檢查當(dāng)前線程時(shí)否是持鎖線程,是的話則直接獲取鎖(可重入鎖的實(shí)現(xiàn)),更新同步資源狀態(tài),如果均失敗,則返回false。
acquireQueued
acquireQueued在AQS中關(guān)聯(lián)的核心代碼如下所示,可以看出主要包含addWaiter和acquireQueued兩部分:
addWaiter
// AbstractQueuedSynchronizer.java
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
// AbstractQueuedSynchronizer.java
private Node addWaiter(Node mode) {
// 創(chuàng)建新的Node對(duì)象
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
可以看到不管是addWaiter還是enq,其最終目標(biāo)都是基于當(dāng)前線程創(chuàng)建新的Node對(duì)象,將新的Node對(duì)象添加在隊(duì)尾,前文提到AQS中維護(hù)了一個(gè)先進(jìn)先出的隊(duì)列,其數(shù)據(jù)結(jié)構(gòu)本質(zhì)是雙向鏈表,這里的head,tail就是鏈表的具體實(shí)現(xiàn)。Node類中包含了指向前一個(gè)元素和后一個(gè)元素的引用,Node的聲明如下:
// Node實(shí)體類
static final class Node {
...
// 前一個(gè)元素
volatile Node prev;
// 下一個(gè)元素
volatile Node next;
// 線程對(duì)象
volatile Thread thread;
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
...
}
// AbstractQueuedSynchronizer.java中聲明Node對(duì)象
// 鏈表頭
private transient volatile Node head;
// 鏈表尾部
private transient volatile Node tail;
acquireQueued
acquireQueued源碼如下:
// AbstractQueuedSynchronizer.java
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
可以看到在addWaiter中成功將新的Node添加到隊(duì)尾后,acquireQueued中當(dāng)前線程會(huì)自旋,嘗試獲取鎖,如果獲取失敗,則執(zhí)行shouldParkAfterFailedAcquire和parkAndCheckInterrupt,這兩個(gè)函數(shù)都返回true則執(zhí)行selfInterrupt方法(代碼見acquire函數(shù)部分)。
shouldParkAfterFailedAcquire用于判斷是否應(yīng)該對(duì)當(dāng)前線程阻塞,如果是的話則返回true,parkAndCheckInterrupt用于執(zhí)行線程阻塞并且判斷當(dāng)前線程是否處于interrupt狀態(tài),如果是則會(huì)返回true。通過(guò)源碼可以看到其是通過(guò)LockSupport.park進(jìn)行線程狀態(tài)切換的,代碼如下:
// AbstractQueuedSynchronizer.java
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
LockSupport.park作用
LockSupport.park用于在許可證不可用時(shí)阻塞禁用當(dāng)前線程,如果許可證可用,則該許可證被消耗,并且當(dāng)前調(diào)用立即返回,否則,出于線程調(diào)度的目的,當(dāng)前線程將被阻塞禁用,并進(jìn)入休眠狀態(tài),直到發(fā)生以下三種情況之一:
- 其他線程以當(dāng)前線程為參數(shù)調(diào)用unpark
- 其他線程中斷當(dāng)前線程
- 調(diào)用發(fā)生異常
結(jié)合上文,我們可以得出ReentrantLock.lock執(zhí)行的一般流程如下所示:

ReentrantLock.unlock流程分析
跟蹤ReentrantLock.unlock調(diào)用流程,可以得到下面的時(shí)序圖(以非公平鎖為例分析):

可以看到對(duì)于ReentrantLock.unlock流程而言,其核心實(shí)現(xiàn)函數(shù)是tryRelease和unparkSuccessor,release函數(shù)如下所示:
// ReentrantLock.java
public void unlock() {
sync.release(1);
}
// AQS.java
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
從代碼中可以看到當(dāng)tryRelease執(zhí)行完成返回true后,我們會(huì)獲取鏈表首位元素,當(dāng)首位元素不為空且等待狀態(tài)(waitStatus)不等于0時(shí),執(zhí)行unparkSuccessor,在這里我們又遇到了Node類的另一個(gè)核心元素waitStatus,其聲明如下:
static final class Node {
...
// waitStatus的四種可能取值,表示當(dāng)前節(jié)點(diǎn)及其后續(xù)節(jié)點(diǎn)對(duì)應(yīng)線程的運(yùn)行狀態(tài)
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
// 聲明waitStatus
volatile int waitStatus;
...
}
四種取值含義如下所示:
- SIGNAL:取值為-1,該節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)處于被阻塞或即將阻塞的狀態(tài),因此當(dāng)前節(jié)點(diǎn)的線程在釋放鎖或取消時(shí)必須解除后繼節(jié)點(diǎn)的阻塞狀態(tài),使后續(xù)節(jié)點(diǎn)的線程得以正常運(yùn)行
- CANCELLED:取值為1,當(dāng)前節(jié)點(diǎn)對(duì)應(yīng)的線程由于超時(shí)或中斷而被取消,Node的waitStatus取該值,進(jìn)入取消狀態(tài)后節(jié)點(diǎn)狀態(tài)不再變化
- CONDITION:取值為-2,該節(jié)點(diǎn)當(dāng)前在等待隊(duì)列中,節(jié)點(diǎn)對(duì)應(yīng)的線程等待Condition,當(dāng)其他線程對(duì)Condition調(diào)用了signal方法后,該節(jié)點(diǎn)從等待隊(duì)列中轉(zhuǎn)入鏈表中,進(jìn)行同步狀態(tài)的獲取
- PROPAGATE:取值為-3,在共享鎖實(shí)現(xiàn)中使用,當(dāng)前節(jié)點(diǎn)線程處于可運(yùn)行狀態(tài)
為了簡(jiǎn)化使用,對(duì)于waitStatus取值并沒(méi)有按照數(shù)字遞增或遞減排列進(jìn)行取值,如果該節(jié)點(diǎn)取值為非負(fù)值,則代表不需要將操作同步到其他節(jié)點(diǎn),對(duì)于普通Node節(jié)點(diǎn)而言,waitStatus字段初始化為0,對(duì)于條件節(jié)點(diǎn),該字段初始化為1,在代碼中使用CAS對(duì)該字段進(jìn)行修改。
下面我們來(lái)分別看下這兩個(gè)核心函數(shù)的實(shí)現(xiàn)
tryRelease
tryRelease主要用于對(duì)鎖狀態(tài)標(biāo)記進(jìn)行清理,函數(shù)實(shí)現(xiàn)如下所示:
protected final boolean tryRelease(int releases) {
// 獲取AQS的鎖狀態(tài)標(biāo)記,計(jì)算剩余鎖狀態(tài)標(biāo)記
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 鎖狀態(tài)標(biāo)記為0,當(dāng)前沒(méi)有其他線程持有鎖,鎖處于空閑狀態(tài),設(shè)置鎖持有者為null
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
// 更新鎖狀態(tài)
setState(c);
return free;
}
在ReentrantLock獨(dú)占鎖實(shí)現(xiàn)的場(chǎng)景下,state鎖狀態(tài)標(biāo)記取值只有兩個(gè),0和1(前文lock過(guò)程中也有分析通過(guò)CAS改變state狀態(tài)時(shí),一直是從0到1的修改),進(jìn)而當(dāng)當(dāng)前線程執(zhí)行tryRelease后,state鎖狀態(tài)標(biāo)記取值更新為0,表示當(dāng)前鎖處于空閑狀態(tài),隨后自然要喚醒Node鏈表中的其他節(jié)點(diǎn)去獲取鎖啦。
unparkSuccessor
unparkSuccessor函數(shù)主要用于更新Node節(jié)點(diǎn)的waitStatus狀態(tài)并按需將Node鏈表中阻塞的線程喚醒執(zhí)行,實(shí)現(xiàn)如下所示:
private void unparkSuccessor(Node node) {
// 獲取頭節(jié)點(diǎn)的waitStatus狀態(tài),將頭節(jié)點(diǎn)的waitStatus狀態(tài)設(shè)置為0,head waitStatus恢復(fù)初始狀態(tài)
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 獲取頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),如果下一個(gè)節(jié)點(diǎn)為null或者狀態(tài)為取消狀態(tài)(waitStatus大于0只有取消狀態(tài)),則從尾節(jié)點(diǎn)開始遍歷,查找未被取消的后續(xù)節(jié)點(diǎn)對(duì)象
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// 對(duì)上一步獲取到的節(jié)點(diǎn)對(duì)應(yīng)的線程執(zhí)行unpark喚醒,去搶占鎖
if (s != null)
LockSupport.unpark(s.thread);
}
從前面可以看出這里傳入的node是當(dāng)前的head(鏈表頭),判斷當(dāng)前頭節(jié)點(diǎn)的waitStatus,如果不是初始值則重置為初始值,隨著查找下一個(gè)要被喚醒的節(jié)點(diǎn),查找到后,喚醒節(jié)點(diǎn)對(duì)應(yīng)的線程,讓線程去嘗試搶占鎖。
結(jié)合上文,我們可以得出ReentrantLock.unlock執(zhí)行的一般流程如下所示:

以上就是ReentrantLock 非公平鎖實(shí)現(xiàn)原理詳解的詳細(xì)內(nèi)容,更多關(guān)于ReentrantLock 非公平鎖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JavaWeb請(qǐng)求轉(zhuǎn)發(fā)和請(qǐng)求包含實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了JavaWeb請(qǐng)求轉(zhuǎn)發(fā)和請(qǐng)求包含實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
淺談Java中生產(chǎn)者與消費(fèi)者問(wèn)題的演變
這篇文章主要介紹了淺談Java中生產(chǎn)者與消費(fèi)者問(wèn)題的演變,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-10-10
解決異常處理問(wèn)題:getReader()?has?already?been?called?for?this
這篇文章主要介紹了解決異常處理:getReader()?has?already?been?called?for?this問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
SpringCloudConfig之client端報(bào)錯(cuò)Could?not?resolve?placeholder問(wèn)
這篇文章主要介紹了SpringCloudConfig之client端報(bào)錯(cuò)Could?not?resolve?placeholder?‘from‘?in?value?“${from}“問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助2022-12-12
Spring Boot應(yīng)用Docker化的步驟詳解
這篇文章主要給大家介紹了關(guān)于Spring Boot應(yīng)用Docker化的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2018-04-04
講解Java設(shè)計(jì)模式編程中的建造者模式與原型模式
這篇文章主要介紹了Java設(shè)計(jì)模式編程中的建造者模式與原型模式,設(shè)計(jì)模式有利于團(tuán)隊(duì)開發(fā)過(guò)程中的代碼維護(hù),需要的朋友可以參考下2016-02-02
關(guān)于java中@Async異步調(diào)用詳細(xì)解析附代碼
本文主要介紹了java關(guān)于@Async異步調(diào)用詳細(xì)解析附代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07

