一、讀寫鎖簡介
現實中有這樣一種場景:對共享資源有讀和寫的操作,且寫操作沒有讀操作那么頻繁,在沒有寫操作的時候,多個執行緒同時讀一個資源沒有任何問題,所以應該允許多個執行緒同時讀取共享資源;但是如果一個執行緒想去寫這些共享資源,就不應該允許其他執行緒對該資源進行讀和寫的操作了,
針對這種場景,JAVA的并發包提供了讀寫鎖ReentrantReadWriteLock,它表示兩個鎖,一個是讀操作相關的鎖,稱為共享鎖;一個是寫相關的鎖,稱為排他鎖,描述如下:
執行緒進入讀鎖的前提條件:
沒有其他執行緒的寫鎖,
沒有寫請求或者有寫請求,但呼叫執行緒和持有鎖的執行緒是同一個,
執行緒進入寫鎖的前提條件:
沒有其他執行緒的讀鎖
沒有其他執行緒的寫鎖
而讀寫鎖有以下三個重要的特性:
(1)公平選擇性:支持非公平(默認)和公平的鎖獲取方式,吞吐量還是非公平優于公平,
(2)重進入:讀鎖和寫鎖都支持執行緒重進入,
(3)鎖降級:遵循獲取寫鎖、獲取讀鎖再釋放寫鎖的次序,寫鎖能夠降級成為讀鎖,
二、原始碼解讀
我們先來看下 ReentrantReadWriteLock 類的整體結構:
public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializable {
/** 讀鎖 */
private final ReentrantReadWriteLock.ReadLock readerLock;
/** 寫鎖 */
private final ReentrantReadWriteLock.WriteLock writerLock;
final Sync sync;
/** 使用默認(非公平)的排序屬性創建一個新的 ReentrantReadWriteLock */
public ReentrantReadWriteLock() {
this(false);
}
/** 使用給定的公平策略創建一個新的 ReentrantReadWriteLock */
public ReentrantReadWriteLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
readerLock = new ReadLock(this);
writerLock = new WriteLock(this);
}
/** 回傳用于寫入操作的鎖 */
public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
/** 回傳用于讀取操作的鎖 */
public ReentrantReadWriteLock.ReadLock readLock() { return readerLock; }
abstract static class Sync extends AbstractQueuedSynchronizer {}
static final class NonfairSync extends Sync {}
static final class FairSync extends Sync {}
public static class ReadLock implements Lock, java.io.Serializable {}
public static class WriteLock implements Lock, java.io.Serializable {}
}
1、類的繼承關系
public class ReentrantReadWriteLock
implements ReadWriteLock, java.io.Serializable {}
說明:可以看到,ReentrantReadWriteLock實作了ReadWriteLock介面,ReadWriteLock介面定義了獲取讀鎖和寫鎖的規范,具體需要實作類去實作;同時其還實作了Serializable介面,表示可以進行序列化,在源代碼中可以看到ReentrantReadWriteLock實作了自己的序列化邏輯,
2、類的內部類
ReentrantReadWriteLock有五個內部類,五個內部類之間也是相互關聯的,內部類的關系如下圖所示,

說明:如上圖所示,Sync繼承自AQS、NonfairSync繼承自Sync類、FairSync繼承自Sync類(通過建構式傳入的布林值決定要構造哪一種Sync實體);ReadLock實作了Lock介面、WriteLock也實作了Lock介面,
Sync類:
(1)類的繼承關系
abstract static class Sync extends AbstractQueuedSynchronizer {}
說明:Sync抽象類繼承自AQS抽象類,Sync類提供了對ReentrantReadWriteLock的支持,
(2)類的內部類
Sync類內部存在兩個內部類,分別為HoldCounter和ThreadLocalHoldCounter,其中HoldCounter主要與讀鎖配套使用,其中,HoldCounter原始碼如下,
// 計數器
static final class HoldCounter {
// 計數
int count = 0;
// Use id, not reference, to avoid garbage retention
// 獲取當前執行緒的TID屬性的值
final long tid = getThreadId(Thread.currentThread());
}
說明:HoldCounter主要有兩個屬性,count和tid,其中count表示某個讀執行緒重入的次數,tid表示該執行緒的tid欄位的值,該欄位可以用來唯一標識一個執行緒,ThreadLocalHoldCounter的原始碼如下
// 本地執行緒計數器
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
// 重寫初始化方法,在沒有進行set的情況下,獲取的都是該HoldCounter值
public HoldCounter initialValue() {
return new HoldCounter();
}
}
說明:ThreadLocalHoldCounter重寫了ThreadLocal的initialValue方法,ThreadLocal類可以將執行緒與物件相關聯,在沒有進行set的情況下,get到的均是initialValue方法里面生成的那個HolderCounter物件,
(3)類的屬性
abstract static class Sync extends AbstractQueuedSynchronizer {
// 版本序列號
private static final long serialVersionUID = 6317671515068378041L;
// 高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;
// 本地執行緒計數器
private transient ThreadLocalHoldCounter readHolds;
// 快取的計數器
private transient HoldCounter cachedHoldCounter;
// 第一個讀執行緒
private transient Thread firstReader = null;
// 第一個讀執行緒的計數
private transient int firstReaderHoldCount;
}
說明:該屬性中包括了讀鎖、寫鎖執行緒的最大量,本地執行緒計數器等,
(4)類的建構式
// 建構式
Sync() {
// 本地執行緒計數器
readHolds = new ThreadLocalHoldCounter();
// 設定AQS的狀態
setState(getState()); // ensures visibility of readHolds
}
說明:在Sync的建構式中設定了本地執行緒計數器和AQS的狀態state,
3、讀寫狀態的設計
同步狀態在重入鎖的實作中是表示被同一個執行緒重復獲取的次數,即一個整形變數來維護,但是之前的那個表示僅僅表示是否鎖定,而不用區分是讀鎖還是寫鎖,而讀寫鎖需要在同步狀態(一個整形變數)上維護多個讀執行緒和一個寫執行緒的狀態,
讀寫鎖對于同步狀態的實作是在一個整形變數上通過“按位切割使用”:將變數切割成兩部分,高16位表示讀,低16位表示寫,

假設當前同步狀態值為S,get和set的操作如下:
(1)獲取寫狀態:
S&0x0000FFFF:將高16位全部抹去
(2)獲取讀狀態:
S>>>16:無符號補0,右移16位
(3)寫狀態加1:
S+1
(4)讀狀態加1:
S+(1<<16)即S + 0x00010000
在代碼層的判斷中,如果S不等于0,當寫狀態(S&0x0000FFFF),而讀狀態(S>>>16)大于0,則表示該讀寫鎖的讀鎖已被獲取,
4、寫鎖的獲取與釋放
看下WriteLock類中的lock和unlock方法:
public void lock() {
sync.acquire(1);
}
public void unlock() {
sync.release(1);
}
可以看到就是呼叫的獨占式同步狀態的獲取與釋放,因此真實的實作就是Sync的 tryAcquire和 tryRelease,
寫鎖的獲取,看下tryAcquire:
1 protected final boolean tryAcquire(int acquires) {
2 //當前執行緒
3 Thread current = Thread.currentThread();
4 //獲取狀態
5 int c = getState();
6 //寫執行緒數量(即獲取獨占鎖的重入數)
7 int w = exclusiveCount(c);
8
9 //當前同步狀態state != 0,說明已經有其他執行緒獲取了讀鎖或寫鎖
10 if (c != 0) {
11 // 當前state不為0,此時:如果寫鎖狀態為0說明讀鎖此時被占用回傳false;
12 // 如果寫鎖狀態不為0且寫鎖沒有被當前執行緒持有回傳false
13 if (w == 0 || current != getExclusiveOwnerThread())
14 return false;
15
16 //判斷同一執行緒獲取寫鎖是否超過最大次數(65535),支持可重入
17 if (w + exclusiveCount(acquires) > MAX_COUNT)
18 throw new Error("Maximum lock count exceeded");
19 //更新狀態
20 //此時當前執行緒已持有寫鎖,現在是重入,所以只需要修改鎖的數量即可,
21 setState(c + acquires);
22 return true;
23 }
24
25 //到這里說明此時c=0,讀鎖和寫鎖都沒有被獲取
26 //writerShouldBlock表示是否阻塞
27 if (writerShouldBlock() ||
28 !compareAndSetState(c, c + acquires))
29 return false;
30
31 //設定鎖為當前執行緒所有
32 setExclusiveOwnerThread(current);
33 return true;
34 }
其中exclusiveCount方法表示占有寫鎖的執行緒數量,原始碼如下:
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
說明:直接將狀態state和(2^16 - 1)做與運算,其等效于將state模上2^16,寫鎖數量由state的低十六位表示,
從源代碼可以看出,獲取寫鎖的步驟如下:
(1)首先獲取c、w,c表示當前鎖狀態;w表示寫執行緒數量,然后判斷同步狀態state是否為0,如果state!=0,說明已經有其他執行緒獲取了讀鎖或寫鎖,執行(2);否則執行(5),
(2)如果鎖狀態不為零(c != 0),而寫鎖的狀態為0(w = 0),說明讀鎖此時被其他執行緒占用,所以當前執行緒不能獲取寫鎖,自然回傳false,或者鎖狀態不為零,而寫鎖的狀態也不為0,但是獲取寫鎖的執行緒不是當前執行緒,則當前執行緒也不能獲取寫鎖,
(3)判斷當前執行緒獲取寫鎖是否超過最大次數,若超過,拋例外,反之更新同步狀態(此時當前執行緒已獲取寫鎖,更新是執行緒安全的),回傳true,
(4)如果state為0,此時讀鎖或寫鎖都沒有被獲取,判斷是否需要阻塞(公平和非公平方式實作不同),在非公平策略下總是不會被阻塞,在公平策略下會進行判斷(判斷同步佇列中是否有等待時間更長的執行緒,若存在,則需要被阻塞,否則,無需阻塞),如果不需要阻塞,則CAS更新同步狀態,若CAS成功則回傳true,失敗則說明鎖被別的執行緒搶去了,回傳false,如果需要阻塞則也回傳false,
(5)成功獲取寫鎖后,將當前執行緒設定為占有寫鎖的執行緒,回傳true,
方法流程圖如下:

寫鎖的釋放,tryRelease方法:
1 protected final boolean tryRelease(int releases) {
2 //若鎖的持有者不是當前執行緒,拋出例外
3 if (!isHeldExclusively())
4 throw new IllegalMonitorStateException();
5 //寫鎖的新執行緒數
6 int nextc = getState() - releases;
7 //如果獨占模式重入數為0了,說明獨占模式被釋放
8 boolean free = exclusiveCount(nextc) == 0;
9 if (free)
10 //若寫鎖的新執行緒數為0,則將鎖的持有者設定為null
11 setExclusiveOwnerThread(null);
12 //設定寫鎖的新執行緒數
13 //不管獨占模式是否被釋放,更新獨占重入數
14 setState(nextc);
15 return free;
16 }
寫鎖的釋放程序還是相對而言比較簡單的:首先查看當前執行緒是否為寫鎖的持有者,如果不是拋出例外,然后檢查釋放后寫鎖的執行緒數是否為0,如果為0則表示寫鎖空閑了,釋放鎖資源將鎖的持有執行緒設定為null,否則釋放僅僅只是一次重入鎖而已,并不能將寫鎖的執行緒清空,
說明:此方法用于釋放寫鎖資源,首先會判斷該執行緒是否為獨占執行緒,若不為獨占執行緒,則拋出例外,否則,計算釋放資源后的寫鎖的數量,若為0,表示成功釋放,資源不將被占用,否則,表示資源還被占用,其方法流程圖如下,

5、讀鎖的獲取與釋放
類似于寫鎖,讀鎖的lock和unlock的實際實作對應Sync的 tryAcquireShared 和 tryReleaseShared方法,
讀鎖的獲取,看下tryAcquireShared方法
1 protected final int tryAcquireShared(int unused) {
2 // 獲取當前執行緒
3 Thread current = Thread.currentThread();
4 // 獲取狀態
5 int c = getState();
6
7 //如果寫鎖執行緒數 != 0 ,且獨占鎖不是當前執行緒則回傳失敗,因為存在鎖降級
8 if (exclusiveCount(c) != 0 &&
9 getExclusiveOwnerThread() != current)
10 return -1;
11 // 讀鎖數量
12 int r = sharedCount(c);
13 /*
14 * readerShouldBlock():讀鎖是否需要等待(公平鎖原則)
15 * r < MAX_COUNT:持有執行緒小于最大數(65535)
16 * compareAndSetState(c, c + SHARED_UNIT):設定讀取鎖狀態
17 */
18 // 讀執行緒是否應該被阻塞、并且小于最大值、并且比較設定成功
19 if (!readerShouldBlock() &&
20 r < MAX_COUNT &&
21 compareAndSetState(c, c + SHARED_UNIT)) {
22 //r == 0,表示第一個讀鎖執行緒,第一個讀鎖firstRead是不會加入到readHolds中
23 if (r == 0) { // 讀鎖數量為0
24 // 設定第一個讀執行緒
25 firstReader = current;
26 // 讀執行緒占用的資源數為1
27 firstReaderHoldCount = 1;
28 } else if (firstReader == current) { // 當前執行緒為第一個讀執行緒,表示第一個讀鎖執行緒重入
29 // 占用資源數加1
30 firstReaderHoldCount++;
31 } else { // 讀鎖數量不為0并且不為當前執行緒
32 // 獲取計數器
33 HoldCounter rh = cachedHoldCounter;
34 // 計數器為慷訓者計數器的tid不為當前正在運行的執行緒的tid
35 if (rh == null || rh.tid != getThreadId(current))
36 // 獲取當前執行緒對應的計數器
37 cachedHoldCounter = rh = readHolds.get();
38 else if (rh.count == 0) // 計數為0
39 //加入到readHolds中
40 readHolds.set(rh);
41 //計數+1
42 rh.count++;
43 }
44 return 1;
45 }
46 return fullTryAcquireShared(current);
47 }
其中sharedCount方法表示占有讀鎖的執行緒數量,原始碼如下:
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
說明:直接將state右移16位,就可以得到讀鎖的執行緒數量,因為state的高16位表示讀鎖,對應的第十六位表示寫鎖數量,
讀鎖獲取鎖的程序比寫鎖稍微復雜些,首先判斷寫鎖是否為0并且當前執行緒不占有獨占鎖,直接回傳;否則,判斷讀執行緒是否需要被阻塞并且讀鎖數量是否小于最大值并且比較設定狀態成功,若當前沒有讀鎖,則設定第一個讀執行緒firstReader和firstReaderHoldCount;若當前執行緒執行緒為第一個讀執行緒,則增加firstReaderHoldCount;否則,將設定當前執行緒對應的HoldCounter物件的值,流程圖如下,

注意:更新成功后會在firstReaderHoldCount中或readHolds(ThreadLocal型別的)的本執行緒副本中記錄當前執行緒重入數(23行至43行代碼),這是為了實作jdk1.6中加入的getReadHoldCount()方法的,這個方法能獲取當前執行緒重入共享鎖的次數(state中記錄的是多個執行緒的總重入次數),加入了這個方法讓代碼復雜了不少,但是其原理還是很簡單的:如果當前只有一個執行緒的話,還不需要動用ThreadLocal,直接往firstReaderHoldCount這個成員變數里存重入數,當有第二個執行緒來的時候,就要動用ThreadLocal變數readHolds了,每個執行緒擁有自己的副本,用來保存自己的重入數,
fullTryAcquireShared方法:
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) { // 無限回圈
// 獲取狀態
int c = getState();
if (exclusiveCount(c) != 0) { // 寫執行緒數量不為0
if (getExclusiveOwnerThread() != current) // 不為當前執行緒
return -1;
} else if (readerShouldBlock()) { // 寫執行緒數量為0并且讀執行緒被阻塞
// 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)) { // 計數器為慷訓者計數器的tid不為當前正在運行的執行緒的tid
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount(c) == MAX_COUNT) // 讀鎖數量為最大值,拋出例外
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { // 比較并且設定成功
if (sharedCount(c) == 0) { // 讀執行緒數量為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;
}
}
}
說明:在tryAcquireShared函式中,如果下列三個條件不滿足(讀執行緒是否應該被阻塞、小于最大值、比較設定成功)則會進行fullTryAcquireShared函式中,它用來保證相關操作可以成功,其邏輯與tryAcquireShared邏輯類似,不再累贅,
讀鎖的釋放,tryReleaseShared方法
1 protected final boolean tryReleaseShared(int unused) {
2 // 獲取當前執行緒
3 Thread current = Thread.currentThread();
4 if (firstReader == current) { // 當前執行緒為第一個讀執行緒
5 // assert firstReaderHoldCount > 0;
6 if (firstReaderHoldCount == 1) // 讀執行緒占用的資源數為1
7 firstReader = null;
8 else // 減少占用的資源
9 firstReaderHoldCount--;
10 } else { // 當前執行緒不為第一個讀執行緒
11 // 獲取快取的計數器
12 HoldCounter rh = cachedHoldCounter;
13 if (rh == null || rh.tid != getThreadId(current)) // 計數器為慷訓者計數器的tid不為當前正在運行的執行緒的tid
14 // 獲取當前執行緒對應的計數器
15 rh = readHolds.get();
16 // 獲取計數
17 int count = rh.count;
18 if (count <= 1) { // 計數小于等于1
19 // 移除
20 readHolds.remove();
21 if (count <= 0) // 計數小于等于0,拋出例外
22 throw unmatchedUnlockException();
23 }
24 // 減少計數
25 --rh.count;
26 }
27 for (;;) { // 無限回圈
28 // 獲取狀態
29 int c = getState();
30 // 獲取狀態
31 int nextc = c - SHARED_UNIT;
32 if (compareAndSetState(c, nextc)) // 比較并進行設定
33 // Releasing the read lock has no effect on readers,
34 // but it may allow waiting writers to proceed if
35 // both read and write locks are now free.
36 return nextc == 0;
37 }
38 }
說明:此方法表示讀鎖執行緒釋放鎖,首先判斷當前執行緒是否為第一個讀執行緒firstReader,若是,則判斷第一個讀執行緒占有的資源數firstReaderHoldCount是否為1,若是,則設定第一個讀執行緒firstReader為空,否則,將第一個讀執行緒占有的資源數firstReaderHoldCount減1;若當前執行緒不是第一個讀執行緒,那么首先會獲取快取計數器(上一個讀鎖執行緒對應的計數器 ),若計數器為慷訓者tid不等于當前執行緒的tid值,則獲取當前執行緒的計數器,如果計數器的計數count小于等于1,則移除當前執行緒對應的計數器,如果計數器的計數count小于等于0,則拋出例外,之后再減少計數即可,無論何種情況,都會進入無限回圈,該回圈可以確保成功設定狀態state,其流程圖如下,

在讀鎖的獲取、釋放程序中,總是會有一個物件存在著,同時該物件在獲取執行緒獲取讀鎖是+1,釋放讀鎖時-1,該物件就是HoldCounter,
要明白HoldCounter就要先明白讀鎖,前面提過讀鎖的內在實作機制就是共享鎖,對于共享鎖其實我們可以稍微的認為它不是一個鎖的概念,它更加像一個計數器的概念,一次共享鎖操作就相當于一次計數器的操作,獲取共享鎖計數器+1,釋放共享鎖計數器-1,只有當執行緒獲取共享鎖后才能對共享鎖進行釋放、重入操作,所以HoldCounter的作用就是當前執行緒持有共享鎖的數量,這個數量必須要與執行緒系結在一起,否則操作其他執行緒鎖就會拋出例外,
先看讀鎖獲取鎖的部分:
if (r == 0) {//r == 0,表示第一個讀鎖執行緒,第一個讀鎖firstRead是不會加入到readHolds中
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {//第一個讀鎖執行緒重入
firstReaderHoldCount++;
} else { //非firstReader計數
HoldCounter rh = cachedHoldCounter;//readHoldCounter快取
//rh == null 或者 rh.tid != current.getId(),需要獲取rh
if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh); //加入到readHolds中
rh.count++; //計數+1
}
這里為什么要搞一個firstRead、firstReaderHoldCount呢?而不是直接使用else那段代碼?這是為了一個效率問題,firstReader是不會放入到readHolds中的,如果讀鎖僅有一個的情況下就會避免查找readHolds,可能就看這個代碼還不是很理解HoldCounter,我們先看firstReader、firstReaderHoldCount的定義:
private transient Thread firstReader = null;
private transient int firstReaderHoldCount;
這兩個變數比較簡單,一個表示執行緒,當然該執行緒是一個特殊的執行緒,一個是firstReader的重入計數,
HoldCounter的定義:
static final class HoldCounter {
int count = 0;
final long tid = Thread.currentThread().getId();
}
在HoldCounter中僅有count和tid兩個變數,其中count代表著計數器,tid是執行緒的id,但是如果要將一個物件和執行緒系結起來僅記錄tid肯定不夠的,而且HoldCounter根本不能起到系結物件的作用,只是記錄執行緒tid而已,
誠然,在java中,我們知道如果要將一個執行緒和物件系結在一起只有ThreadLocal才能實作,所以如下:
static final class ThreadLocalHoldCounter
extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
ThreadLocalHoldCounter繼承ThreadLocal,并且重寫了initialValue方法,
故而,HoldCounter應該就是系結執行緒上的一個計數器,而ThradLocalHoldCounter則是執行緒系結的ThreadLocal,從上面我們可以看到ThreadLocal將HoldCounter系結到當前執行緒上,同時HoldCounter也持有執行緒Id,這樣在釋放鎖的時候才能知道ReadWriteLock里面快取的上一個讀取執行緒(cachedHoldCounter)是否是當前執行緒,這樣做的好處是可以減少ThreadLocal.get()的次數,因為這也是一個耗時操作,需要說明的是這樣HoldCounter系結執行緒id而不系結執行緒物件的原因是避免HoldCounter和ThreadLocal互相系結而GC難以釋放它們(盡管GC能夠智能的發現這種參考而回收它們,但是這需要一定的代價),所以其實這樣做只是為了幫助GC快速回收物件而已,
三、總結
通過上面的原始碼分析,我們可以發現一個現象:
在執行緒持有讀鎖的情況下,該執行緒不能取得寫鎖(因為獲取寫鎖的時候,如果發現當前的讀鎖被占用,就馬上獲取失敗,不管讀鎖是不是被當前執行緒持有),
在執行緒持有寫鎖的情況下,該執行緒可以繼續獲取讀鎖(獲取讀鎖時如果發現寫鎖被占用,只有寫鎖沒有被當前執行緒占用的情況才會獲取失敗),
仔細想想,這個設計是合理的:因為當執行緒獲取讀鎖的時候,可能有其他執行緒同時也在持有讀鎖,因此不能把獲取讀鎖的執行緒“升級”為寫鎖;而對于獲得寫鎖的執行緒,它一定獨占了讀寫鎖,因此可以繼續讓它獲取讀鎖,當它同時獲取了寫鎖和讀鎖后,還可以先釋放寫鎖繼續持有讀鎖,這樣一個寫鎖就“降級”為了讀鎖,
綜上:
一個執行緒要想同時持有寫鎖和讀鎖,必須先獲取寫鎖再獲取讀鎖;寫鎖可以“降級”為讀鎖;讀鎖不能“升級”為寫鎖,
來源:https://www.cnblogs.com/xiaoxi/
歡迎關注公眾號 【碼農開花】一起學習成長
我會一直分享Java干貨,也會分享免費的學習資料課程和面試寶典
回復:【計算機】【設計模式】【面試】有驚喜哦
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/241720.html
標籤:Java
