Java并發(fā)編程ReentrantReadWriteLock加讀鎖流程
正文
protected final int tryAcquireShared(int unused) { Thread current = Thread.currentThread(); int c = getState(); if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) return -1; int r = sharedCount(c); 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); }
上面是嘗試加讀鎖流程的代碼,既然這篇是番外篇,那就不按正常流程一點(diǎn)一點(diǎn)去分析了,著重拿出一部分來分析一下。ReentrantReadWriteLock
和ReentrantLock
相比,除了多了讀寫鎖之外,還增加了很多屬性,比如firstReader
、firstReaderHoldCount
、cachedHoldCounter
......那我們這篇文章就介紹一下這些新屬性的含義以及上面代碼中加鎖成功后的處理。
屬性介紹
static final class HoldCounter { int count = 0; final long tid = getThreadId(Thread.currentThread()); }
HoldCount
類型用來存儲線程ID
和線程持有的讀鎖數(shù)量
private transient ThreadLocalHoldCounter readHolds; static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> { public HoldCounter initialValue() { return new HoldCounter(); } }
readHolds
通過ThreadLocal
在線程本地存儲了一個(gè)HoldCounter
對象,表示當(dāng)前線程持有的讀鎖重入數(shù)量,主要是為了方便在發(fā)生重入或者釋放鎖時(shí),分別計(jì)算每個(gè)線程持有的讀鎖數(shù)量。
private transient HoldCounter cachedHoldCounter;
cachedHoldCounter
存儲的是最后一個(gè)獲取讀鎖成功的線程持有的讀鎖數(shù)量。但是如果只有一個(gè)線程獲取讀鎖,會使用firstReader
和firstReaderHoldCount
來記錄線程持有讀鎖數(shù)量,只有獲取讀鎖的線程數(shù)大于1
時(shí)才會用cachedHoldCounter
存儲最后線程持有的讀鎖數(shù)量。
private transient Thread firstReader = null;
第一個(gè)獲取讀鎖的線程,確切地說是把讀鎖數(shù)量從0
改成1
的線程,并且當(dāng)前還沒有釋放鎖。如果第一個(gè)線程釋放了鎖,就會把firstReader
設(shè)為null
,只有當(dāng)所有讀鎖釋放之后,下一個(gè)獲取讀鎖成功的線程就成為firstReader
。
private transient int firstReaderHoldCount;
第一個(gè)獲取讀鎖的線程持有讀鎖的數(shù)量。
加鎖成功處理
int r = sharedCount(c); 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++; }
這里截取加鎖成功之后處理的代碼來分析下對這些屬性的操作。
if (r == 0)
表示共享鎖數(shù)量為0
,當(dāng)前線程就是第一個(gè)獲取讀鎖成功的線程,所以firstReader
和firstReaderHoldCount
記錄的就是當(dāng)前線程。- 如果讀鎖數(shù)量不是
0
,但是當(dāng)前線程是第一個(gè)線程,那就直接在原來數(shù)量基礎(chǔ)上進(jìn)行累加firstReaderHoldCount++
; - 如果讀鎖數(shù)量不為
0
,而且當(dāng)前線程也不是第一個(gè)線程,這時(shí)就需要用到cachedHoldCounter
了。rh == null
表示當(dāng)前線程是第二個(gè)線程,rh.tid != getThreadId(current)
表示當(dāng)前線程至少是第三個(gè)線程(這里不考慮重入情況,只考慮當(dāng)前線程第一次獲取讀鎖成功),兩個(gè)條件合起來可以理解為之前緩存的最后一個(gè)獲取讀鎖成功的線程不是當(dāng)前線程,所以就需要更新為當(dāng)前線程cachedHoldCounter = rh = readHolds.get()
。- 如果之前緩存的最后一個(gè)線程是當(dāng)前線程,那么就會有一個(gè)特殊情況
rh.count == 0
,這里可以理解為一個(gè)線程釋放了讀鎖之后又重新獲取了讀鎖,釋放完所有鎖時(shí),為了防止內(nèi)存泄漏會調(diào)用readHolds.remove()
清除線程本地存儲的信息,而現(xiàn)在加鎖成功了就需要在線程本地重新記錄持有鎖的數(shù)量,既然緩存的就是當(dāng)前線程的,那就直接用緩存來更新到線程本地就可以了。
以上就是Java并發(fā)編程ReentrantReadWriteLock番外的詳細(xì)內(nèi)容,更多關(guān)于Java并發(fā)ReentrantReadWriteLock的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Mybatis/Mybatis-Plus駝峰式命名映射的實(shí)現(xiàn)
本文主要介紹了Mybatis-Plus駝峰式命名映射的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-07-07Java平臺調(diào)試體系原理分析和實(shí)踐整理 遠(yuǎn)程Debug
這篇文章主要介紹了Java平臺調(diào)試體系原理分析和實(shí)踐整理 遠(yuǎn)程Debug,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-03-03解決Error:(1,?1)?java:?非法字符:?'\ufeff'問題
這篇文章主要介紹了解決Error:(1,?1)?java:?非法字符:?'\ufeff'問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Maven中Junit測試@Test等注解無法識別的問題及解決
這篇文章主要介紹了Maven中Junit測試@Test等注解無法識別的問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11