在AQS的介紹中,鎖分為獨占鎖和共享鎖,在上節中我們介紹了獨占鎖ReentrantLock,本次將針對另一個獨占鎖ReentrantReadWriteLock進行學習。
1. ReadWriteLock
ReadWriteLock屬于獨占鎖,它內部包含一對相互關聯的Lock鎖,分別用于讀寫操作。只要沒有 writer,讀取鎖可以由多個 reader 線程同時保持。寫入鎖是獨占的。
read-write鎖相比互斥鎖提供了更大的并發量級別。雖然一次只有一個線程(writer 線程)可以修改共享數據,但在許多情況下,任何數量的線程可以同時讀取共享數據(reader 線程),讀-寫鎖利用了這一點。從理論上講,與互斥鎖相比,使用讀-寫鎖所允許的并發性增強將帶來更大的性能提高。在實踐中,只有在多處理器上并且只在訪問模式適用于共享數據時,才能完全實現并發性增
相比于互斥鎖,使用read-write鎖提供了更強的并發能力。但是提升性能取決于讀寫操作期間讀取數據相對于修改數據的頻率,以及數據的爭用——即在同一時間試圖對該數據執行讀取或寫入操作的線程數。例如,某個最初用數據填充并且之后不經常對其進行修改的 collection,因為經常對其進行搜索(比如搜索某種目錄),所以這樣的 collection 是使用讀-寫鎖的理想候選者。但是,如果數據更新變得頻繁,數據在大部分時間都被獨占鎖,這時,就算存在并發性增強,也是微不足道的。更進一步地說,如果讀取操作所用時間太短,則讀-寫鎖實現(它本身就比互斥鎖復雜)的開銷將成為主要的執行成本,在許多讀-寫鎖實現仍然通過一小段代碼將所有線程序列化時更是如此。最終,只有通過分析和測量,才能確定應用程序是否適合使用讀-寫鎖。
所以根據上面推斷,讀寫鎖適用于讀取較多、寫比較少的場景。
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*
* @return the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*
* @return the lock used for writing.
*/
Lock writeLock();
}
ReadWriteLock提供了兩個方法分別用于獲取Read-Lock和Write-Lock。
2. ReentrantReadWriteLock特點
ReentrantReadWriteLock與ReentrantLock實現非常類似。它具有以下屬性。
1、鎖獲取順序
ReentrantLock不會將讀取者優先或寫入者優先強加給鎖訪問的排序。但是,它確實支持可選的公平 策略。
2、默認模式,非公平模式
當非公平策略(默認)構造時,未指定進入讀寫鎖的順序,受到 reentrancy 約束的限制。連續競爭的非公平鎖可能無限期地推遲一個或多個 reader 或 writer 線程,但吞吐量通常要高于公平鎖。
3、公平模式
當使用公平策略是,線程使用一種近似同步到達的順序爭奪資源。當線程釋放當前持有鎖時,等待時間最長的write線程獲取到寫入鎖,如果有一組等待時間大于所有正在等待的 writer 線程 的 reader 線程,將為該組分配寫入鎖。
當一個線程嘗試獲取公平策略的read-lock時,如果write-lock被持有或者有等待的write線程,則當前線程會阻塞。直到當前最舊的等待 writer 線程已獲得并釋放了寫入鎖之后,該線程才會獲得讀取鎖。當然,如果等待 writer 放棄其等待,而保留一個或更多 reader 線程為隊列中帶有寫入鎖自由的時間最長的 waiter,則將為那些 reader 分配讀取鎖。
4、鎖降級
重入還允許從寫入鎖降級為讀取鎖,其實現方式是:先獲取寫入鎖,然后獲取讀取鎖,最后釋放寫入鎖。但是,從讀取鎖升級到寫入鎖是不可能的。
class CachedData {
Object data;
volatile boolean cacheValid;
ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
// Recheck state because another thread might have acquired
// write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
rwl.writeLock().unlock(); // Unlock write, still hold read
}
use(data);
rwl.readLock().unlock();
}
}
5、鎖獲取的中斷
讀取鎖和寫入鎖都支持鎖獲取期間的中斷。
6、Condition 支持
寫入鎖提供了一個 Condition 實現,對于寫入鎖來說,該實現的行為與 ReentrantLock.newCondition() 提供的 Condition 實現對 ReentrantLock 所做的行為相同。當然,此 Condition 只能用于寫入鎖。
讀取鎖不支持 Condition,readLock().newCondition() 會拋出 UnsupportedOperationException。
在使用某些種類的 Collection 時,可以使用 ReentrantReadWriteLock 來提高并發性。通常,在預期 collection 很大,讀取者線程訪問它的次數多于寫入者線程,并且 entail 操作的開銷高于同步開銷時,這很值得一試。例如,以下是一個使用 TreeMap 的類,預期它很大,并且能被同時訪問。
class RWDictionary {
private final Map<String, Data> m = new TreeMap<String, Data>();
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private final Lock r = rwl.readLock();
private final Lock w = rwl.writeLock();
public Data get(String key) {
r.lock();
try { return m.get(key); }
finally { r.unlock(); }
}
public String[] allKeys() {
r.lock();
try { return m.keySet().toArray(); }
finally { r.unlock(); }
}
public Data put(String key, Data value) {
w.lock();
try { return m.put(key, value); }
finally { w.unlock(); }
}
public void clear() {
w.lock();
try { m.clear(); }
finally { w.unlock(); }
}
}
實現注意事項
此鎖最多支持 65535 個遞歸寫入鎖和 65535 個讀取鎖。試圖超出這些限制將導致鎖方法拋出 Error。
3. ReentrantReadWriteLock源碼分析
3.1 基本框架結構組成
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {
private static final long serialVersionUID = -6992448646407690164L;
/** Inner class providing readlock */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** Inner class providing writelock */
private final ReentrantReadWriteLock.WriteLock writerLock;
/** Performs all synchronization mechanics */
final Sync sync;
/**
* Creates a new {@code ReentrantReadWriteLock} with
* default (nonfair) ordering properties.
*/
public ReentrantReadWriteLock() {
this(false);
}
/**
* Creates a new {@code ReentrantReadWriteLock} with
* the given fairness policy.
*
* @param fair {@code true} if this lock should use a fair ordering policy
*/
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
.....
}
ReentrantReadWriteLock內部由一個ReadLock對象和一個WriteLock對象組成,分別是由內部類實現。同時包含實現同步功能的Sync對象。在構造方法總,支持無參構造函數和有參構造函數,默認是采用非公平策略。所以根據構造函數中FairSync、NonfairSync、ReadLock和WriteLock這四個類的實現。
因為使用的Visual Studio繪制的UML圖,沒發現內部類嵌套的繪制關系圖標,所以使用組合替代了。
- ReentrantReadWriteLock實現了ReadWriteLock接口。ReadWriteLock是一個讀寫鎖的接口,提供了"獲取讀鎖的readLock()函數" 和 "獲取寫鎖的writeLock()函數"。
- ReentrantReadWriteLock中包含:sync對象,讀鎖readerLock和寫鎖writerLock。讀鎖ReadLock和寫鎖WriteLock都實現了Lock接口。讀鎖ReadLock和寫鎖WriteLock中也都分別包含了"Sync對象",它們的Sync對象和ReentrantReadWriteLock的Sync對象 是一樣的,就是通過sync,讀鎖和寫鎖實現了對同一個對象的訪問。
- 和"ReentrantLock"一樣,sync是Sync類型;而且,Sync也是一個繼承于AQS的抽象類。Sync也包括"公平鎖"FairSync和"非公平鎖"NonfairSync。sync對象是"FairSync"和"NonfairSync"中的一個,默認是"NonfairSync"。
3.2 Sync類的源碼
通過上面的UML類圖關系可以看出,最終ReadLock和WriteLock執行的本質方法都是在Sync類中。
/**
* ReentrantReadWriteLock內部的實現機制
* @author Iflytek_dsw
*
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 6317671515068378041L;
/**
* 這些量用來計算Read和Write鎖的數量,ReentrantReadWriterLock使用一個32位的int類型來表示鎖被占用的線程數
* (ReentrantLock中的state)采取的辦法是:
* 高16位用來表示讀鎖(共享鎖)占有的線程數量,用低16位表示寫鎖(獨占鎖)被占用的數量
*/
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT);
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
/**
* 通過進行右移16位計算出共享鎖(讀取鎖)的占用個數
*/
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/**
* 通過進行&運算計算出低16位的寫鎖(獨占鎖)個數
*/
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
/**
* 定義一個容器來計算保存每個線程read鎖的個數
*/
static final class HoldCounter {
int count = 0;
// Use id, not reference, to avoid garbage retention
final long tid = Thread.currentThread().getId();
}
/**
* ThreadLocal的使用來針對每個線程進行存儲HoldCounter
*/
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
/**
* 一個read線程的HoldCounter,當為0的時候進行刪除。
*/
private transient ThreadLocalHoldCounter readHolds;
/**
* cachedHoldCounter 緩存的是最后一個獲取線程的HolderCount信息,
* 該變量主要是在如果當前線程多次獲取讀鎖時,減少從readHolds中獲取HoldCounter的次數
*/
private transient HoldCounter cachedHoldCounter;
/**
* firstReader is the first thread to have acquired the read lock.
* firstReaderHoldCount is firstReader's hold count.
*/
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
Sync() {
readHolds = new ThreadLocalHoldCounter();
setState(getState()); // ensures visibility of readHolds
}
/**
* Returns true if the current thread, when trying to acquire
* the read lock, and otherwise eligible to do so, should block
* because of policy for overtaking other waiting threads.
*/
abstract boolean readerShouldBlock();
/**
* Returns true if the current thread, when trying to acquire
* the write lock, and otherwise eligible to do so, should block
* because of policy for overtaking other waiting threads.
*/
abstract boolean writerShouldBlock();
protected final boolean tryRelease(int releases) {
/**當前線程不支持鎖的時候,拋IllegalMonitor異常*/
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
/**當前鎖線程的個數是否為0個*/
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}
protected final boolean tryAcquire(int acquires) {
//省略
}
protected final boolean tryReleaseShared(int unused) {
//省略
}
private IllegalMonitorStateException unmatchedUnlockException() {
return new IllegalMonitorStateException(
"attempt to unlock read lock, not locked by current thread");
}
protected final int tryAcquireShared(int unused) {
//省略
}
/**
* Full version of acquire for reads, that handles CAS misses
* and reentrant reads not dealt with in tryAcquireShared.
*/
final int fullTryAcquireShared(Thread current) {
//省略
}
/**
* Performs tryLock for write, enabling barging in both modes.
* This is identical in effect to tryAcquire except for lack
* of calls to writerShouldBlock.
*/
final boolean tryWriteLock() {
//省略
}
/**
* Performs tryLock for read, enabling barging in both modes.
* This is identical in effect to tryAcquireShared except for
* lack of calls to readerShouldBlock.
*/
final boolean tryReadLock()
//省略
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
// Methods relayed to outer class
final ConditionObject newCondition() {
return new ConditionObject();
}
final Thread getOwner() {
// Must read state before owner to ensure memory consistency
return ((exclusiveCount(getState()) == 0) ?
null :
getExclusiveOwnerThread());
}
final int getReadLockCount() {
return sharedCount(getState());
}
final boolean isWriteLocked() {
return exclusiveCount(getState()) != 0;
}
final int getWriteHoldCount() {
return isHeldExclusively() ? exclusiveCount(getState()) : 0;
}
final int getReadHoldCount() {
//省略
}
/**
* Reconstitute this lock instance from a stream
* @param s the stream
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
readHolds = new ThreadLocalHoldCounter();
setState(0); // reset to unlocked state
}
final int getCount() { return getState(); }
}
上面的代碼中省略多個關鍵方法的代碼實現,代碼比較多。后面在具體的流程中會單獨挑出來分析。這里我們只要知道在Sync類中定義了如下關鍵方法:
- readerShouldBlock()
- writerShouldBlock()
- tryAcquire(int acquires)
- tryReleaseShared(int unused)
- tryAcquireShared(int unused)
- tryWriteLock()
- tryReadLock()
3.3 FairSync公平鎖
ReentrantReadWriteLock同樣支持公平鎖和非公平鎖。FairSync的實現如下:
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
在Sync類中預留了writerShouldBlock()和readerShouldBlock()兩個抽象方法供子類重寫。在FairSync中返回的是hasQueuedPredecessors()方法的返回值。
/**
* 當前線程前面是否有處理的線程,如果有返回true,反之返回false。
* 隊列為空同樣返回false
*/
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
3.4 NonfairSync非公平鎖
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();
}
}
在非公平鎖中,write返回的是false。而在讀鎖中則返回apparentlyFirstQueuedIsExclusive()方法。
final boolean apparentlyFirstQueuedIsExclusive() {
Node h, s;
return (h = head) != null &&
(s = h.next) != null &&
!s.isShared() &&
s.thread != null;
}
該方法如果頭節點不為空,并頭節點的下一個節點不為空,并且不是共享模式【獨占模式,寫鎖】、并且線程不為空,則返回true。
這個方法判斷隊列的head.next是否正在等待獨占鎖(寫鎖,因為在ReentrantReadWriteLock中讀寫鎖共用一個隊列)。當然這個方法執行的過程中隊列的形態可能發生變化。這個方法的意思是:讀鎖不應該讓寫鎖始終等待,因為在同一時刻讀寫鎖只能有一種鎖在“工作”。
3.5 ReadLock源碼解讀
ReadLock是讀取鎖的獲取。
public static class ReadLock implements Lock, java.io.Serializable {
private static final long serialVersionUID = -5992448646407690164L;
private final Sync sync;
/**
* Constructor for use by subclasses
*
* @param lock the outer lock object
* @throws NullPointerException if the lock is null
*/
protected ReadLock(ReentrantReadWriteLock lock) {
sync = lock.sync;
}
/**
* Acquires the read lock.
* 申請read鎖
* 如果write鎖沒有被別的線程持有,則立即返回read鎖。如果write鎖被占用,則當前線程會被阻塞,直至獲取到read鎖。
*/
public void lock() {
sync.acquireShared(1);
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
/**
* Acquires the read lock only if the write lock is not held by
* another thread at the time of invocation.
*
* <p>If the write lock is held by another thread then
* this method will return immediately with the value
* {@code false}.
*
* @return {@code true} if the read lock was acquired
*/
public boolean tryLock() {
return sync.tryReadLock();
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
/**
* Attempts to release this lock.
*
* <p> If the number of readers is now zero then the lock
* is made available for write lock attempts.
*/
public void unlock() {
sync.releaseShared(1);
}
/**
* Throws {@code UnsupportedOperationException} because
* {@code ReadLocks} do not support conditions.
*
* @throws UnsupportedOperationException always
*/
public Condition newCondition() {
throw new UnsupportedOperationException();
}
/**
* Returns a string identifying this lock, as well as its lock state.
* The state, in brackets, includes the String {@code "Read locks ="}
* followed by the number of held read locks.
*
* @return a string identifying this lock, as well as its lock state
*/
public String toString() {
int r = sync.getReadLockCount();
return super.toString() +
"[Read locks = " + r + "]";
}
}
ReadLock繼承Lock類,并實現了對應的申請所、釋放鎖方法。
3.5.1 ReadLock申請鎖lock
public void lock() {
sync.acquireShared(1);
}
read鎖是共享鎖,這里調用sync對象的acquireShared()方法來申請鎖。
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
這里tryAcquireShared方法執行就到了Sync類中。
protected final int tryAcquireShared(int unused) {
/*
* Walkthrough:
* 1. 如果write鎖被其它線程占用,申請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,并且持有線程不等于當前線程,返回-1*/
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
/**獲取讀取鎖(共享鎖)的個數*/
int r = sharedCount(c);
/** 如果當前read鎖不被阻塞,并且個數小于最大MAX_COUNT,同時CAS操作成功*/
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {//如果當前共享鎖個數為0,沒有被持有
/**當前線程賦值給firstReader,并且firstReadHoldCount賦值為1*/
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
/**如果當前線程已經持有所,則firstReaderHoldCount自加1*/
firstReaderHoldCount++;
} else {/**其它情況則是新來的一個線程*/
/**新建一個HoldCounter對象存儲cachedHoldCounter*/
HoldCounter rh = cachedHoldCounter;
/**如果cachedHoldCounter為空,或tid不等于當前線程*/
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
/**如果鎖被占用等不滿足上述情況,則通過fullTryAcquireShared進行申請*/
return fullTryAcquireShared(current);
}
在tryAcquireShared方法中, 如果當前read鎖不被阻塞,并且個數小于最大MAX_COUNT,同時CAS操作成功則申請鎖成功。反之通過fullTryAcquireShared方法進行申請。
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
/**采用自旋的方式,直至申請到鎖*/
for (;;) {
int c = getState();
/**如果Write鎖被占用,并且持有線程不是當前線程則返回-1*/
if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
// else we hold the exclusive lock; blocking here
// would cause deadlock.
} else if (readerShouldBlock()) {//Write鎖被持有
// 如果最近沒有申請過read鎖
if (firstReader == current) {
// assert firstReaderHoldCount > 0;
} else {//沒有申請過
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId()) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
/**Read鎖已經到達最大值*/
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
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 != current.getId())
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}
采用自旋的方式,其它邏輯同tryAcquireShared類似。
- 如果Write鎖被占有,并且持有寫鎖的線程不是當前線程直接返回-1
- 如果Write鎖當前線程持有,并且在CLH隊列中下一個節點是Write鎖,則返回-1
最后在所有申請失敗返回-1的時候,則通過doAcquireShared()方法進行申請。
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
通過上面的分析ReadLock的lock執行流程如下:
[圖片上傳失敗...(image-c16b-1528795162670)]
3.5.2 tryLock()嘗試申請鎖
前面在ReentrantLock的筆記中,同樣有tryLock方法。其實它們都差不多。
public boolean tryLock() {
return sync.tryReadLock();
}
tryLock的內部調用的Sync類的tryReadLock()方法。
final boolean tryReadLock() {
Thread current = Thread.currentThread();
for (;;) {
int c = getState();
/**Write鎖被占用并且占用線程不是當前線程,返回false*/
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return false;
/**返回read鎖的個數*/
int r = sharedCount(c);
/**如果是最大值,則拋出異常*/
if (r == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {//Read的個數為0,則將當前線程賦值給firstReader,并且firstReaderHoldCount賦值為1
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {//如果firstReader==當前線程,則firstReaderHoldCount自加1
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return true;
}
}
}
3.5.3 鎖的釋放
public void unlock() {
sync.releaseShared(1);
}
unlock釋放鎖最終還是在Sync類的releaseShared方法中進行釋放。releaseShared方法是在AQS類中。
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
在AQS中,我們知道releaseShared方法是AQS預留給子類進行實現的,所以最終的實現還是在Sync類中。
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 != current.getId())
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;
}
}
4 總結
上面我們針對ReadLock的鎖的申請、釋放進行了分析,WriteLock由于是個獨占鎖,大致跟ReentrantLock流程差不多,不做過多分析。總體說來,還是需要多讀幾遍源碼才能理解透徹。
最后給一個使用的示例:
public class MyTest {
static StudentList studentList = new StudentList();
public static void main(String[]args){
for(int i=0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
studentList.addItem();
}
}).start();
}
for(int i=0;i<3;i++){
new Thread(new Runnable() {
@Override
public void run() {
studentList.printList();
}
}).start();
}
}
}
class StudentList{
private List<Integer> listNumber = new ArrayList<Integer>();
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private ReadLock readLock;
private WriteLock writeLock;
public StudentList(){
readLock = reentrantReadWriteLock.readLock();
writeLock = reentrantReadWriteLock.writeLock();
}
public void printList(){
readLock.lock();
for(int i=0;i<listNumber.size();i++){
System.out.println(Thread.currentThread().getName() + "--printList--current:" + i);
}
readLock.unlock();
}
public void addItem(){
writeLock.lock();
for(int i=listNumber.size();i<5;i++){
System.out.println(Thread.currentThread().getName() + "--addItem--current:" + i);
listNumber.add(i);
}
writeLock.unlock();
}
}
執行結果:
Thread-0--addItem--current:0
Thread-0--addItem--current:1
Thread-0--addItem--current:2
Thread-0--addItem--current:3
Thread-0--addItem--current:4
Thread-3--printList--current:0
Thread-3--printList--current:1
Thread-3--printList--current:2
Thread-3--printList--current:3
Thread-3--printList--current:4
Thread-4--printList--current:0
Thread-5--printList--current:0
Thread-4--printList--current:1
Thread-5--printList--current:1
Thread-4--printList--current:2
Thread-5--printList--current:2
Thread-4--printList--current:3
Thread-5--printList--current:3
Thread-4--printList--current:4
Thread-5--printList--current:4
通過運行結果我們可以證明一個結論:Read鎖是共享鎖,每個線程都能獲取到。Write鎖是獨占鎖,只能同時被一個線程持有。
當我們把Write鎖釋放代碼注釋:
public void addItem(){
writeLock.lock();
for(int i=listNumber.size();i<5;i++){
System.out.println(Thread.currentThread().getName() + "--addItem--current:" + i);
listNumber.add(i);
}
//writeLock.unlock();
}
Thread-0--addItem--current:0
Thread-0--addItem--current:1
Thread-0--addItem--current:2
Thread-0--addItem--current:3
Thread-0--addItem--current:4
通過運行結果可以有結論:當Write鎖被持有的時候,Read鎖是無法被其它線程申請的,會處于阻塞狀態。直至Write鎖釋放。同時也可以驗證到當同一個線程持有Write鎖時是可以申請到Read鎖。
推薦閱讀:
JDK1.8源碼分析之ReentrantReadWriteLock(七):https://www.cnblogs.com/leesf456/p/5419132.html