ReentrantLock-基於AQS實現獨佔鎖
阿新 • • 發佈:2020-06-24
1 概述
ReentrantLock是基於AQS實現的一款獨佔鎖,有公平鎖和非公平鎖兩種模式。
預設是使用的非公平鎖:
public ReentrantLock() {
sync = new NonfairSync();
}
複製程式碼
也可指定模式:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
複製程式碼
- 1
ReentrantLock
實現了Lock
介面; - 2
ReentrantLock
定義了內部類Syncabstract static class Sync extends AbstractQueuedSynchronizer{....}
; - 3
Sync
是一個抽象類,有兩個實現類NonfairSync
/FairSync
,分別用來實現非公平鎖/公平鎖。
官方示例:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
複製程式碼
2 實現
本文僅從ReentrantLock的lock/unlock
的實現,分析如何基於AQS實現一個獨佔鎖。
根據AQS約定(可參考AQS原始碼分析及核心方法解析):
- 1 lock:實現
tryAcquire
,並在lock時呼叫acquire
; - 2 unlock:實現
tryRelease
,並在unlock時呼叫release
。
2 lock()
ReentrantLock.lock()
呼叫了Sync.lock()
,而Sync.lock()
是一個抽象方法,由子類實現。
所以NonfairSync
/FairSync
兩個類中,必然使用了acquire
方法去獲取鎖,並實現了tryAcquire
方法。
2.1 NonfairSync
2.1.1 lock
- 1 呼叫
compareAndSetState
將state值由0置換為1,成功的話,說明當前沒有其他執行緒持有鎖,不去排隊,直接持有。通過setExclusiveOwnerThread
將當前執行緒設定為獨佔鎖持有者。 - 2 否則通過
acquire
去排隊。
final void lock() {
if (compareAndSetState(0,1)) // 如果當前state==0,則說明沒有其他執行緒持有鎖,CAS成功。
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
複製程式碼
2.1.2 tryAcquire
NonfairSync.tryAcquire
直接呼叫的Sync.nonfairTryAcquire
。
nonfairTryAcquire
是非公平的tryAcquire
實現方式,不會通過AQS.hasQueuedPredecessors
來判斷是有執行緒在排隊。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// state == 0,說明沒有其他執行緒持有鎖。
if (compareAndSetState(0,acquires)) {
// 將當前執行緒設定為`獨佔鎖持有者`,tryAcqire成功。
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
/*
如果當前執行緒正是`獨佔鎖持有者`,疊加state,實現`可重入`,tryAcqire成功。
也就是說AQS的同步列表中,有多個當前執行緒的節點。
*/
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
複製程式碼
2.2 FairSync
2.2.1 lock
沒有做compareAndSetState
嘗試,直接將自己加入AQS的同步佇列中。
final void lock() {
acquire(1);
}
複製程式碼
2.2.2 tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { // state == 0,說明沒有其他執行緒持有鎖。
if (!hasQueuedPredecessors() &&
compareAndSetState(0,acquires)) {
/*
`!hasQueuedPredecessors()` 說明AQS的同步佇列中,沒有比自己更優先的執行緒在等待
*/
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
/*
如果當前執行緒正是`獨佔鎖持有者`,疊加state,實現`可重入`,tryAcqire成功。
也就是說AQS的同步列表中,有多個當前執行緒的節點。
*/
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
複製程式碼
FairSync.tryAcquire
比NonFairSync.tryAcquire
多了一個!hasQueuedPredecessors()
的判斷,其他流程都是一樣的。
3 unlock()
unlock操作,NonFairSync
/FairSync
中沒有區分,直接在Sync中實現。
Sync
中的unlock
呼叫了AQS.release
,並實現了tryRelease
。
public void unlock() {
sync.release(1);
}
複製程式碼
3.1 tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
// 當前執行緒不是`獨佔鎖持有者`,丟擲異常。
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { // c == 0 說明鎖已經完全釋放。
free = true;
// 將當前`獨佔鎖持有者`置空。
setExclusiveOwnerThread(null);
}
// 更新state值
setState(c);
return free;
}
複製程式碼
4 總結
NonFairSync
/FairSync
基本流程是一樣的,不同的是:
- 1
NonFairSync
在lock時,會先嚐試compareAndSetState(0,1)
搶佔鎖,失敗的話再進行acquire(1)
;FairSync
直接進行acquire(1)
排隊。 - 2
FairSync
在tryAcquire
時,在判斷compareAndSetState(0,acquires)
的同時,多進行了一個hasQueuedPredecessors()
的判斷,用於判斷同步佇列中是否有比自己優先的執行緒。