java併發源碼:ReentrantReadWriteLock
結構
可見ReentrantReadWriteLock實現了ReadWriteLock接口。
ReadWriteLock接口定義了兩個接口:
Lock readLock()
Lock writeLock()
ReentrantLock是排他鎖,在同一時刻只允許一個線程進行訪問,而讀寫鎖在同一時刻可以允許多個線程訪問。但是在寫線程訪問時,所有的讀線程以及寫線程均被堵塞,防止髒讀。讀寫鎖維護了一對鎖,一個讀鎖和一個寫鎖。
內部工作狀態方法:
方法名稱 | 描述 |
---|---|
int getReadLockCount() | 返回當前讀鎖被獲取的次數。該次數不等於獲取讀鎖的線程數。例如:一個線程獲取了N次讀鎖,佔據該讀鎖的線程數是1,返回N。 |
int getReadHoldCount() | 返回當前線程獲取讀鎖的次數。 |
boolean isWriteLocked() | 判斷寫鎖是否被獲取。 |
int getWriteHoldCount() | 返回當前寫鎖被獲取的次數。 |
讀寫狀態的設計
ReentrantLock同步狀態表示鎖被一個線程重複獲取的次數。而讀寫鎖的自定義同步器需要在同步狀態上維護多個讀線程和一個寫線程的狀態。
讀寫鎖將變量切分成了兩個部分,高16位表示讀,低16位表示寫。通過位運算來判斷讀和寫的狀態。假設當前狀態是S,寫狀態等於S & 0x0000FFFF(將高16位全部抹去),讀狀態等於S>>>16。當寫狀態增加1時,等於S+1,當讀狀態增加1時,等於S+(1<<16),也就是S+0x00010000。
寫鎖的獲取和釋放
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();
//獲取共享變量state
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
//當前線程持有寫鎖,爲重入鎖,+acquires即可
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
如果存在讀鎖,則寫鎖不能被獲取。避免髒讀發生。
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;
}
讀鎖的獲取與釋放
讀鎖是一個支持重進入的共享鎖,他能被多個線程同時獲取。
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();
//寫鎖不等於0的情況下,驗證是否是當前寫鎖嘗試獲取讀鎖
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
//獲取讀鎖數量
int r = sharedCount(c);
//CAS操作嘗試設置獲取讀鎖 也就是高位加1
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
//當前線程第一個並且第一次獲取讀鎖,
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
//當前線程是第一次獲取讀鎖的線程
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// 當前線程不是第一個獲取讀鎖的線程,放入線程本地變量
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}
如果有線程獲取了寫鎖,則當前線程獲取讀鎖失敗,進入等待狀態。如果當前線程獲取了寫鎖或者寫鎖未被獲取,則當前線程增加讀狀態, 成功獲取讀鎖。
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 (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
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.
return nextc == 0;
}
}
讀鎖的每次釋放均減少讀狀態,減少的值爲(1<<16)
鎖降級
鎖降級是指寫鎖降級爲讀鎖。如果當前線程擁有寫鎖,然後將其釋放,最後再獲取讀鎖,這種分段完成的過程不能稱之爲鎖降級。鎖降級是指:擁有寫鎖,再獲取讀鎖,隨後釋放寫鎖的過程。
鎖降級中讀鎖的獲取是否必要:主要是爲了保證數據的可見性,如果當前線程不獲取讀鎖而是直接釋放寫鎖,假設此刻另一個線程(T)獲取了寫鎖,並修改了數據,那麼當前線程則無法感知到線程T的數據更新。如果當前線程獲取讀鎖,則遵循鎖降級的步驟,則線程T會被阻塞。直到當前線程使用數據並釋放 鎖之後,線程T才能獲取寫鎖進行數據更新。