-
讀寫鎖允許同一時刻被多個讀線程訪問,但是在寫線程訪問時,所有的讀線程和其他的寫線程都會被阻塞。
-
ReentrantReadWriteLock特性:
- 公平性選擇:支持非公平性(默認)和公平的鎖獲取方式,吞吐量還是非公平優於公平;
- 重入性:支持重入,讀鎖獲取後能再次獲取,寫鎖獲取之後能夠再次獲取寫鎖,同時也能夠獲取讀鎖;
- 鎖降級:遵循獲取寫鎖,獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成爲讀鎖。
-
閱讀之前的一些思考:
- 讀寫鎖是怎樣記錄讀寫狀態?
- 寫鎖怎樣獲取鎖、釋放鎖?
- 讀鎖怎樣獲取鎖、釋放鎖?
- 類關係圖如下所示:
-
獲取讀鎖
- ReentrantReadWriteLock->ReadLock.lock()->Sync.acquireShared(1)->AQS->tryAcquireShared(arg)
-
場景:多線程安全操作下,讀的情況多於寫的情況,滿足多線程操作的安全性,也要確保性能不能太差。
// AQS中的獲取共享鎖的方法
public final void acquireShared(int arg) {
// 小於0表示獲取失敗進入進入隊列
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// ReentrantReadWriteLock->Sync中獲取共享鎖的實現
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. If write lock held by another thread, fail.
* 2. Otherwise, this thread is eligible for
* lock wrt state, so ask if it should block
* because of queue policy. If not, try
* to grant by CASing state and updating count.
* Note that step does not check for reentrant
* acquires, which is postponed to full version
* to avoid having to check hold count in
* the more typical non-reentrant case.
* 3. If step 2 fails either because thread
* apparently not eligible or CAS fails or count
* saturated, chain to version with full retry loop.
*/
Thread current = Thread.currentThread();
// 獲取鎖當前狀態
int c = getState();
// 如果當前有線程獲取了寫鎖,且不是當前線程,返回失敗
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
// 獲取當前讀鎖的數量,讀狀態存儲在高位
// c >>> 16取高16位的值
int r = sharedCount(c);
// 是否阻塞判斷
// 鎖數量沒有達到最大值
// 嘗試CAS將狀態修改爲c+65536, 修改成功則獲取鎖
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
// 如果誰也沒有獲取過讀鎖
if (r == 0) {
// 第一個reader指向當前線程,讀鎖持有數量爲1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
// 如果當前線程再次獲取到讀鎖,持有數量+1
firstReaderHoldCount++;
} else {
// 獲取讀鎖的不是第一個線程,記錄每個線程獲取的次數,存到線程的ThreadLocalMap中
HoldCounter rh = cachedHoldCounter;
// 緩存爲空 || 緩存的不是當前線程的讀鎖計數
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
// 緩存的是當前線程的讀鎖計數,而當前線程的讀鎖計數在上次release時被刪除
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
// 循環嘗試獲取鎖
return fullTryAcquireShared(current);
}
// 默認進入非公平鎖代碼段
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false; // writers can always barge
}
final boolean readerShouldBlock() {
/* As a heuristic to avoid indefinite writer starvation,
* block if the thread that momentarily appears to be head
* of queue, if one exists, is a waiting writer. This is
* only a probabilistic effect since a new reader will not
* block if there is a waiting writer behind other enabled
* readers that have not yet drained from the queue.
*/
return apparentlyFirstQueuedIsExclusive();
}
}
// 這裏返回false,則不進行阻塞
// 如果頭節點爲空
// 如果頭結點不爲空,但頭結點後面節點爲空
// 如果頭結點不爲空,後繼節點不爲空,後繼節點是共享的
// 如果頭結點不爲空,後繼節點不爲空,後繼節點不是共享的,後繼節點中線程爲空
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*/
final int fullTryAcquireShared(Thread current) {
/*
* This code is in part redundant with that in
* tryAcquireShared but is simpler overall by not
* complicating tryAcquireShared with interactions between
* retries and lazily reading hold counts.
*/
HoldCounter rh = null;
for (;;) {
int c = getState();
// 有線程獲取寫鎖,且不是當前線程返回失敗
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
// 讀線程需要阻塞
} else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly
// 當前線程佔有了鎖
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {
// 其它線程佔有了鎖
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
// 當前線程的讀鎖計數爲0
if (rh.count == 0)
// 在線程局部變量中刪除當前線程的讀鎖計數
readHolds.remove();
}
}
// 當前線程的讀鎖計數爲0
if (rh.count == 0)
// 應排隊取鎖
return -1;
}
}
// 讀鎖達到最大值,異常
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 嘗試CAS更新狀態,成功則返回
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount(c) == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
- 釋放讀鎖
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
// 嘗試釋放讀鎖
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
// 當前線程爲首個讀線程
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
// 在線程局部變量中刪除當前線程的讀鎖計數
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
// 當前線程的讀鎖計數(緩存的線程讀鎖計數)--
--rh.count;
}
for (;;) {
// CAS失敗將回到此處
int c = getState();
// 讀鎖計數--
int nextc = c - SHARED_UNIT;
// CAS重置讀鎖status
if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
// CAS(state)成功
return nextc == 0;
}
}
-
獲取寫鎖
- 主要邏輯爲:當讀鎖已經被讀線程獲取或者寫鎖已經被其他寫線程獲取,則寫鎖獲取失敗;否則,獲取成功並支持重入,增加寫狀態。
// WriteLock
public void lock() {
sync.acquire(1);
}
// AQS
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// Sync
protected final boolean tryAcquire(int acquires) {
/*
* Walkthrough:
* 1. If read count nonzero or write count nonzero
* and owner is a different thread, fail.
* 2. If count would saturate, fail. (This can only
* happen if count is already nonzero.)
* 3. Otherwise, this thread is eligible for lock if
* it is either a reentrant acquire or
* queue policy allows it. If so, update state
* and set owner.
*/
Thread current = Thread.currentThread();
// 獲取當前鎖的狀態
int c = getState();
// 獲取寫鎖的次數
int w = exclusiveCount(c);
if (c != 0) {
// (Note: if c != 0 and w == 0 then shared count != 0)
// 當前線程不是獲取寫鎖的線程 或者有線程獲取到讀鎖
if (w == 0 || current != getExclusiveOwnerThread())
return false;
// 寫鎖數量大於最大值,拋異常
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// Reentrant acquire
// 當前線程獲取寫鎖
// ??? 當前線程已經獲取寫鎖,且沒有其它線程獲取到讀鎖
setState(c + acquires);
return true;
}
// 寫鎖未被任何線程獲取,CAS成功
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
static final int SHARED_SHIFT = 16;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
// 同步狀態的低16位用來表示寫鎖的獲取次數
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
// 1 & ((1<<16)-1)
// 同步狀態的高16位用來表示讀鎖被獲取的次數
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
// 2>>> 16
- 釋放寫鎖
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 同步狀態減去寫狀態
int nextc = getState() - releases;
// 當前寫狀態是否爲0,爲0則釋放寫鎖
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
// 不爲0則更新同步狀態
setState(nextc);
return free;
}