上篇的中篇主要講的ReentrantReadWriteLock
的ReadLock
的加鎖和減鎖過程:
源碼解讀之(五)ReentrantReadWriteLock(中)
這篇下篇主要來講解講解ReentrantReadWriteLock
的WriteLock
的加鎖和減鎖過程。
四、WriteLock
ReentrantReadWriteLock
的WriteLock
加鎖解鎖過程依賴於AbstractQueuedSynchronizer(AQS)
類,所以有些相同的邏輯可以看看ReentrantLock的邏輯。
1、加鎖過程
- WriteLock的lock()內部通過sync.acquire(1)獲取鎖。
- 嘗試通過tryAcquire獲取寫鎖,如果獲取成功那麼就成功佔用寫鎖。
- 獲取寫鎖失敗後,將當前線程添加到寫鎖喚醒隊列當中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();
}
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);
}
}
tryAcquire過程
- 獲取鎖狀態state變量,並獲取寫鎖佔用的計數值。
- 當前state不爲0,如果寫鎖狀態爲0說明讀鎖被佔用,返回鎖佔用失敗。
- 鎖狀態state不爲空且佔鎖線程爲當前線程,說明鎖被其他線程佔用返回鎖佔用失敗。
- 寫鎖重入數溢出,返回鎖佔用失敗。
- 如果寫鎖阻塞 或者 設置state狀態失敗,返回鎖佔用失敗。
- 設置當前鎖佔用線程爲當前線程,返回鎖佔用成功。
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
// 獲取鎖狀態state變量
int c = getState();
// 獲取寫鎖佔用的計數
int w = exclusiveCount(c);
// 如果鎖狀態state不爲0
if (c != 0) {
// 1、當前state不爲0,如果寫鎖狀態爲0說明讀鎖此時被佔用,說明鎖被讀鎖佔用
// 2、鎖狀態state不爲空且佔鎖線程爲當前線程(屬於鎖重入),說明鎖被其他線程佔用
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 寫鎖重入數溢出
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 寫鎖獲取成功返回成功標記
setState(c + acquires);
return true;
}
// 如果寫鎖阻塞 或者 設置state狀態失敗,那麼就代表獲鎖失敗
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
// 設置當前鎖佔用線程爲當前線程
setExclusiveOwnerThread(current);
return true;
}
2、解鎖過程
- WriteLock的unlock()內部通過sync. release(1)釋放鎖。
- 嘗試通過tryRelease()方法來釋放鎖並喚醒下一個等待線程。
- 在喚醒過程中需要仔細看看讀寫鎖等待線程喚醒的細節。
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;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 如果狀態取消,從尾部向後遍歷以找到實際的未取消的節點
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;
}
if (s != null)
LockSupport.unpark(s.thread);
}
tryRelease過程
- 判斷當前線程和鎖佔用線程不一致isHeldExclusively()拋出異常。
- 鎖狀態減去當前釋放動作傳入參數nextc = getState() - releases。
- 判斷鎖狀態的寫狀態爲0就表明當前線程已經完全釋放鎖。
- 當前線程完全釋放鎖,然後設置鎖佔用線程爲null並設置鎖狀態。
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;
}