開篇
?這篇文章主要從源碼角度講解ReentrantReadWriteLock的WriteLock的加鎖和減鎖過程号涯。
?ReentrantReadWriteLock的WriteLock加鎖解鎖過程依賴于AbstractQueuedSynchronizer(AQS)類,所以有些相同的邏輯可以看看ReentrantLock的邏輯五慈。
- ReentrantReadWriteLock的數(shù)據(jù)結(jié)構(gòu)介紹
- java源碼 - ReentrantReadWriteLock讀鎖介紹
- java源碼 - ReentrantReadWriteLock寫鎖介紹
加鎖過程
- WriteLock的lock()內(nèi)部通過sync.acquire(1)獲取鎖蝎土。
- 嘗試通過tryAcquire獲取寫鎖慨代,如果獲取成功那么就成功占用寫鎖息罗。
- 獲取寫鎖失敗后,將當(dāng)前線程添加到寫鎖喚醒隊(duì)列當(dāng)中acquireQueued(addWaiter(Node.EXCLUSIVE), arg))锥累。
- acquireQueued(addWaiter(Node.EXCLUSIVE), arg))的操作邏輯和ReentrantLock的邏輯一致征懈。
public static class WriteLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -4992448646407690164L;
private final Sync sync;
protected WriteLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
public void lock() {
sync.acquire(1);
}
}
public final void acquire(int arg) {
// 1、先嘗試獲取鎖tryAcquire
// 2揩悄、獲鎖失敗就addWaiter操作
// 3卖哎、acquireQueued判斷是否喚醒
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire過程
- 獲取鎖狀態(tài)state變量,并獲取寫鎖占用的計(jì)數(shù)值删性。
- 當(dāng)前state不為0亏娜,如果寫鎖狀態(tài)為0說明讀鎖被占用,返回鎖占用失敗蹬挺。
- 鎖狀態(tài)state不為空且占鎖線程為當(dāng)前線程维贺,說明鎖被其他線程占用返回鎖占用失敗。
- 寫鎖重入數(shù)溢出巴帮,返回鎖占用失敗溯泣。
- 如果寫鎖阻塞 或者 設(shè)置state狀態(tài)失敗,返回鎖占用失敗榕茧。
- 設(shè)置當(dāng)前鎖占用線程為當(dāng)前線程垃沦,返回鎖占用成功。
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
// 獲取鎖狀態(tài)state變量
int c = getState();
// 獲取寫鎖占用的計(jì)數(shù)
int w = exclusiveCount(c);
// 如果鎖狀態(tài)state不為0
if (c != 0) {
// 1用押、當(dāng)前state不為0肢簿,如果寫鎖狀態(tài)為0說明讀鎖此時被占用,說明鎖被讀鎖占用
// 2蜻拨、鎖狀態(tài)state不為空且占鎖線程為當(dāng)前線程(屬于鎖重入)池充,說明鎖被其他線程占用
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 寫鎖重入數(shù)溢出
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 寫鎖獲取成功返回成功標(biāo)記
setState(c + acquires);
return true;
}
// 如果寫鎖阻塞 或者 設(shè)置state狀態(tài)失敗,那么就代表獲鎖失敗
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 設(shè)置當(dāng)前鎖占用線程為當(dāng)前線程
setExclusiveOwnerThread(current);
return true;
}
解鎖過程
- WriteLock的unlock()內(nèi)部通過sync. release(1)釋放鎖缎讼。
- 嘗試通過tryRelease()方法來釋放鎖并喚醒下一個等待線程收夸。
- 在喚醒過程中需要仔細(xì)看看讀寫鎖等待線程喚醒的細(xì)節(jié),待補(bǔ)充
public void unlock() {
sync.release(1);
}
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease過程
- 判斷當(dāng)前線程和鎖占用線程不一致isHeldExclusively()拋出異常血崭。
- 鎖狀態(tài)減去當(dāng)前釋放動作傳入?yún)?shù)nextc = getState() - releases卧惜。
- 判斷鎖狀態(tài)的寫狀態(tài)為0就表明當(dāng)前線程已經(jīng)完全釋放鎖。
- 當(dāng)前線程完全釋放鎖功氨,然后設(shè)置鎖占用線程為null并設(shè)置鎖狀態(tài)序苏。
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}