???????對(duì)于一般的鎖骂维,如ReentrantLock,其一般都是“獨(dú)占式”的扼脐,也即在同一時(shí)刻只有一個(gè)線程能夠訪問鎖定的代碼肠仪。如果對(duì)于共享資源的訪問“讀”多于“寫”肖抱,那么獨(dú)占鎖將沒有“讀寫鎖”有效。所謂“讀寫鎖”指的是對(duì)共享資源的訪問提供一個(gè)讀鎖和一個(gè)寫鎖藤韵,當(dāng)訪問方式是讀取操作時(shí)虐沥,使用讀鎖即可,當(dāng)訪問方式是修改操作時(shí)泽艘,則使用寫鎖欲险。讀鎖和寫鎖相互之間存在一定的制約條件:
- 當(dāng)有讀鎖鎖定資源時(shí),其余的讀鎖可以共享式的訪問資源匹涮,但是會(huì)阻塞寫鎖對(duì)資源的獲忍焓浴;
- 當(dāng)有寫鎖鎖定資源時(shí)然低,將阻塞其余所有的讀鎖(除了該線程本身)和寫鎖的獲认裁俊;
???????在Java中雳攘,提供了一個(gè)通用的接口來表示讀寫鎖带兜,即ReadWriteLock,該類提供了兩個(gè)方法:readLock()和writeLock()用于對(duì)讀鎖和寫鎖的獲取吨灭,具體的聲明如下:
public interface ReadWriteLock {
Lock readLock();
Lock writeLock();
}
???????除此之外刚照,Java還提供了該接口的兩個(gè)實(shí)現(xiàn):ReadWriteLockView和ReentrantReadWriteLock。第一個(gè)類是StampedLock類的內(nèi)部類喧兄,第二個(gè)則是我們正澄夼希可以使用的類,這里我們主要對(duì)ReentrantReadWriteLock進(jìn)行講解吠冤。
???????ReentrantReadWriteLock是一個(gè)復(fù)合詞浑彰,其拆分開來為reentrant read write lock。reentrant是entrant加上re前綴拯辙,表示可重入的郭变,read write lock則表示其是一個(gè)讀寫鎖,整體理解也即是“可重入的讀寫鎖”涯保《希可以看出,ReentrantReadWriteLock不僅實(shí)現(xiàn)了ReadWriteLock接口的規(guī)范遭赂,并且還提供了可重入的特性。如下是使用ReentrantReadWriteLock實(shí)現(xiàn)對(duì)緩存數(shù)據(jù)的訪問的一個(gè)簡(jiǎn)單示例:
public class MapCache<T> {
private ReadWriteLock lock = new ReentrantReadWriteLock();
private Map<String, T> cache = new HashMap<>();
public void put(String key, T value) {
lock.writeLock().lock();
try {
cache.put(key, value);
} finally {
lock.writeLock().unlock();
}
}
public T get(String key) {
lock.readLock().lock();
try {
T result = cache.get(key);
return result;
} finally {
lock.readLock().unlock();
}
}
}
???????可以看到横辆,在對(duì)緩存數(shù)據(jù)進(jìn)行讀取時(shí)使用讀鎖撇他,而進(jìn)行修改時(shí)則使用寫鎖茄猫。下面為了展示讀寫鎖的是如何工作的,我們編寫了如下示例:
public class ReadWriteLockTest {
public static void main(String[] args) {
RWLCache cache = new RWLCache();
Random random = new Random();
for (int i = 0; i < 10; i++) {
boolean flag = random.nextBoolean();
Thread thread = new Thread(generateTask(flag, cache), generateThreadName(flag, i));
thread.start();
}
}
private static Runnable generateTask(boolean flag, RWLCache cache) {
Runnable putTask = () -> cache.write();
Runnable getTask = () -> cache.read();
return flag ? putTask : getTask;
}
private static String generateThreadName(boolean flag, int index) {
return flag ? "write-" + index : "read-" + index;
}
}
???????其中RWLCache的具體實(shí)現(xiàn)如下:
public class RWLCache {
private MyReentrantReadWriteLock lock = new MyReentrantReadWriteLock();
public void write() {
lock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " acquires write lock, queued threads is " + getThreadNames(lock.getQueuedThreads()));
sleep(1);
} finally {
System.out.println(Thread.currentThread().getName() + " releases write lock");
lock.writeLock().unlock();
}
}
public void read() {
lock.readLock().lock();
try {
sleep(1);
System.out.println(Thread.currentThread().getName() + " acquires read lock, queued threads is " + getThreadNames(lock.getQueuedThreads()));
} finally {
System.out.println(Thread.currentThread().getName() + " releases read lock");
lock.readLock().unlock();
}
}
private static final class MyReentrantReadWriteLock extends ReentrantReadWriteLock {
@Override
protected Collection<Thread> getQueuedThreads() {
List<Thread> threads = new ArrayList<>(super.getQueuedThreads());
Collections.reverse(threads);
return threads;
}
}
private void sleep(int seconds) {
try {
TimeUnit.SECONDS.sleep(seconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private String getThreadNames(Collection<Thread> threads) {
StringBuilder result = new StringBuilder("[");
Iterator<Thread> iterator = threads.iterator();
if (!threads.isEmpty()) {
Thread thread = iterator.next();
result.append(thread.getName());
}
while (iterator.hasNext()) {
Thread thread = iterator.next();
result.append(",").append(thread.getName());
}
return result.append("]").toString();
}
}
???????可以看到困肩,在每次讀寫鎖的獲取和釋放時(shí)划纽,我們都打印了等待隊(duì)列中的線程,如下是該程序的輸出結(jié)果:
read-0 acquires read lock, queued threads is [write-2,read-3,write-4,write-5,read-6,read-7,write-8,read-9]
read-1 acquires read lock, queued threads is [write-2,read-3,write-4,write-5,read-6,read-7,write-8,read-9]
read-1 releases read lock
read-0 releases read lock
write-2 acquires write lock, queued threads is [read-3,write-4,write-5,read-6,read-7,write-8,read-9]
write-2 releases write lock
read-3 acquires read lock, queued threads is [write-4,write-5,read-6,read-7,write-8,read-9]
read-3 releases read lock
write-4 acquires write lock, queued threads is [write-5,read-6,read-7,write-8,read-9]
write-4 releases write lock
write-5 acquires write lock, queued threads is [read-6,read-7,write-8,read-9]
write-5 releases write lock
read-7 acquires read lock, queued threads is [write-8,read-9]
read-7 releases read lock
read-6 acquires read lock, queued threads is [write-8,read-9]
read-6 releases read lock
write-8 acquires write lock, queued threads is [read-9]
write-8 releases write lock
read-9 acquires read lock, queued threads is []
read-9 releases read lock
???????從輸出結(jié)果看出锌畸,read-0和read-0兩個(gè)線程在還未釋放讀鎖的情況下同時(shí)獲取到了讀鎖勇劣,而后續(xù)的寫鎖則被阻塞在隊(duì)列中的;從寫線程對(duì)鎖的獲取和釋放可以看出潭枣,其在執(zhí)行過程中是會(huì)阻塞其余的讀鎖和寫鎖的比默;另外,仔細(xì)觀察read-6和read-7這兩個(gè)線程盆犁,其在等待隊(duì)列中的順序是read-6在前命咐,而read-7在后,但是在后續(xù)輸出的時(shí)候是read-7在前谐岁,而read-6在后醋奠,這是因?yàn)樵趓ead-6獲取到讀鎖之后,由于讀鎖是共享的伊佃,其會(huì)喚醒后續(xù)緊接著的嘗試獲取讀鎖的線程窜司,因而read-6和read-7幾乎是同時(shí)獲取到讀鎖的,但是read-7首先獲取到了cpu的執(zhí)行權(quán)限航揉,因而其先打印了其執(zhí)行代碼塞祈,而read-6則在獲取到cpu的執(zhí)行權(quán)限后輸出。
???????從上面的示例代碼可以看出迷捧,ReentrantReadWriteLock主要有四個(gè)方法:ReadLock.lock()织咧,ReadLock.unlock(),WriteLock.lock()和WriteLock.unlock()漠秋。這里ReadLock.lock()和ReadLock.unlock()是嘗試對(duì)讀鎖的獲取和釋放的笙蒙,WriteLock.lock()和WriteLock.unlock()是嘗試對(duì)寫鎖的獲取和釋放的。下面我們會(huì)對(duì)這四個(gè)方法分別進(jìn)行介紹庆锦,首先我們先來看看ReentrantReadWriteLock的類結(jié)構(gòu)圖:
???????從圖中可以看出捅位,ReadLock和WriteLock都是ReentrantReadWriteLock的內(nèi)部類,其主要是提供對(duì)寫鎖的鎖的獲取和釋放相關(guān)的接口的搂抒。首先我們看看幾個(gè)輔助的作用:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -8159625535654395037L;
final boolean writerShouldBlock() {
return false;
}
final boolean readerShouldBlock() {
return apparentlyFirstQueuedIsExclusive();
}
}
static final class FairSync extends Sync {
private static final long serialVersionUID = -2274990926593161451L;
final boolean writerShouldBlock() {
return hasQueuedPredecessors();
}
final boolean readerShouldBlock() {
return hasQueuedPredecessors();
}
}
???????FairSync和NonfairSync兩個(gè)類用于表征當(dāng)前的讀寫鎖是公平的還是非公平的艇搀。非公平表示線程獲取讀鎖和寫鎖的概率是隨機(jī)的,也即誰先爭(zhēng)取到執(zhí)行權(quán)限就先執(zhí)行鎖定的代碼求晶;公平鎖表示線程所有線程獲取讀鎖和寫鎖的概率都是一樣的焰雕,當(dāng)線程嘗試獲取鎖的權(quán)限時(shí),其會(huì)被加入到一個(gè)等待隊(duì)列中芳杏,每次都是隊(duì)列頭部的線程最開始執(zhí)行鎖定代碼矩屁。一般的辟宗,公平鎖雖不能保證每個(gè)線程都有相同的機(jī)會(huì)執(zhí)行代碼,但是其能夠提高系統(tǒng)的吞吐率吝秕,但是其會(huì)提高造成死鎖的概率泊脐;公平鎖則能夠保證每個(gè)線程都有均等的機(jī)會(huì)執(zhí)行代碼,其也能降低造成死鎖的概率烁峭,但是其吞吐率沒有非公平鎖的高容客。
static final class HoldCounter {
int count = 0;
final long tid = getThreadId(Thread.currentThread());
}
???????HoldCounter是一個(gè)計(jì)數(shù)輔助類,其count字段記錄了每個(gè)線程重入讀鎖或?qū)戞i的次數(shù)约郁,tid字段則記錄了當(dāng)前是記錄的哪個(gè)線程缩挑。
static final class ThreadLocalHoldCounter extends ThreadLocal<HoldCounter> {
public HoldCounter initialValue() {
return new HoldCounter();
}
}
???????ThreadLocalHoldCounter繼承自ThreadLocal,并且重寫了initialValue()方法棍现,也就是說每個(gè)線程使用該類時(shí)都有一個(gè)默認(rèn)的HoldCounter計(jì)數(shù)類调煎。這里ThreadLocalHoldCounter主要是為每個(gè)線程保存其計(jì)數(shù)器對(duì)象的。
???????ReentrantReadWriteLock內(nèi)部?jī)?nèi)部也是基于AbstractQueuedSynchronizer實(shí)現(xiàn)的己肮,這里Sync也就是該類的子類士袄。AbstractQueuedSynchronizer內(nèi)部是通過一個(gè)volatile類型的整型變量state來控制鎖的獲取的,這里volatile能夠保證多線程環(huán)境下該變量對(duì)多個(gè)線程都可見谎僻,而整型int占32個(gè)字節(jié)娄柳,無論是32位還是64位處理器,其都能夠保證每次處理的最小字節(jié)單元是32位艘绍,也就是說對(duì)該變量的更新操作都是原子的赤拒。AbstractQueuedSynchronizer主要提供了如下幾個(gè)方法:
public final void acquire(int arg);
public final void acquireShared(int arg);
protected int tryAcquire(int arg);
protected int tryAcquireShared(int arg);
public final boolean release(int arg);
public final boolean releaseShared(int arg);
protected boolean tryRelease(int arg);
protected boolean tryReleaseShared(int arg);
???????可以看到,這些方法都是成對(duì)呈現(xiàn)的诱鞠,AbstractQueuedSynchronizer使用了模板方法模式來實(shí)現(xiàn)對(duì)state屬性的控制的挎挖。這里acquire(int)和acquireShared(int)方法是對(duì)外的接口,分別表示以非共享或者共享的方式獲取鎖的執(zhí)行權(quán)限航夺。tryAcquire(int)和tryAcquireShared(int)方法則是提供的鉤子方法蕉朵,子類按照需要實(shí)現(xiàn)這兩個(gè)方法,這兩個(gè)方法分別是acquire(int)和acquireShared(int)方法調(diào)用的阳掐,用于控制當(dāng)前線程是否獲取到了鎖的執(zhí)行權(quán)限始衅,如果獲取到了,則當(dāng)前線程繼續(xù)執(zhí)行后續(xù)代碼缭保,如果沒有則均通過CAS算法獲取鎖的權(quán)限汛闸。同理,release(int)和releaseShared(int)方法也是對(duì)外提供的接口艺骂,分別表示以非共享和共享的方式釋放鎖的權(quán)限诸老。tryRelease(int)和tryReleaseShared(int)方法則也是鉤子方法,由子類實(shí)現(xiàn)钳恕,其分別是由release(int)和releaseShared(int)方法調(diào)用别伏,而其作用則分別為以非共享或者共享的方式釋放當(dāng)前鎖的執(zhí)行權(quán)限吮廉。首先我們看看ReentrantReadWriteLock內(nèi)部Sync的tryAcquire(int)方法的具體實(shí)現(xiàn):
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c); // 獲取當(dāng)前正在占用寫鎖的次數(shù)
if (c != 0) { // c不等于0表示當(dāng)前肯定有讀鎖或者寫鎖被占用
if (w == 0 || current != getExclusiveOwnerThread()) // 當(dāng)前沒有寫鎖占用或者占用寫鎖的不是當(dāng)前線程
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT) // 占用寫鎖的次數(shù)超過最大限制次數(shù)
throw new Error("Maximum lock count exceeded");
setState(c + acquires); // 走到這一步說明當(dāng)前線程是正在占用寫鎖的線程,那么成功獲取鎖的權(quán)限
return true;
}
// 這里說明c==0畸肆,也即沒有線程占用鎖,那么判斷寫鎖是否應(yīng)該被阻塞宙址,并且判斷能否通過CAS算法設(shè)置state屬性值
// 這里writerShouldBlock()也即前面講的FairSync和NoneFairSync中重寫的方法
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current); // 設(shè)置當(dāng)前獨(dú)占鎖的線程為當(dāng)前線程
return true;
}
???????這里需要說明的是轴脐,由于Sync通過state屬性控制兩個(gè)鎖,這就需要兩種狀態(tài)抡砂,因而Sync將state屬性分為兩部分:高16位和低16位大咱。高16位的值指定了讀鎖的占用次數(shù),低16位的值則指定了寫鎖的占用次數(shù)注益,并且這里還可以推斷出碴巾,如果state不為0,而低16位等于0丑搔,那么說明高16位一定不為零厦瓢,也就是說當(dāng)前有線程正在占用讀鎖。為了對(duì)讀寫狀態(tài)的獲取方便啤月,Sync中聲明了幾個(gè)變量和相關(guān)的方法:
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;
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
???????可以看出煮仇,sharedCount(int)方法用于獲取高16位的數(shù)值,exclusiveCount(int)用于獲取低16位的數(shù)值谎仲,并且這里MAX_COUNT表示讀鎖和寫鎖最多獲取的次數(shù)浙垫。
???????我們?cè)倩剡^頭來看tryAcquire(int)方法,該方法表示一個(gè)線程能否以獨(dú)占的方式獲取鎖的執(zhí)行權(quán)限郑诺,其首先判斷c是否不為0夹姥,然后在該if條件中判斷是否有線程占用讀鎖,或者是占用寫鎖的線程不是當(dāng)前線程辙诞,如果成立則表示當(dāng)前線程獲取寫鎖失敗辙售,這也就是為什么獲取了寫鎖的線程將會(huì)阻塞其余的讀線程和寫線程的原因;如果第一個(gè)條件不成立倘要,則說明當(dāng)前線程是已經(jīng)占用寫鎖的線程圾亏,那么就判斷已經(jīng)占用的次數(shù)(也即重入的次數(shù))加上此次占用的次數(shù)是否會(huì)超過最大占用次數(shù),如果不成立封拧,則表示當(dāng)前線程可以成功(再次)獲取寫鎖的權(quán)限志鹃,那么就設(shè)置state屬性的值為新的值,并返回true泽西。如果c為0曹铃,則說明當(dāng)前沒有線程占用讀鎖和寫鎖,那么就判斷寫鎖是否應(yīng)該被阻塞捧杉,或者能否通過CAS算法成功設(shè)置state為新的屬性值陕见。如果寫鎖不應(yīng)該被阻塞秘血,并且成功更新了state的值,那么表示當(dāng)前線程獲取寫鎖成功评甜,則設(shè)置獨(dú)占鎖的擁有者為當(dāng)前線程灰粮。這里需要說明的是,writerShouldBlock()方法是Sync的子類FairSync和NoneFairSync分別重寫了的方法忍坷。由前面的代碼可以看出粘舟,F(xiàn)airSync中該方法是判斷當(dāng)前阻塞隊(duì)列中是否有等待的線程,如果有等待的線程佩研,則返回true柑肴,也就表示當(dāng)前線程應(yīng)該被阻塞,因?yàn)楦鶕?jù)公平鎖的協(xié)議旬薯,線程獲取鎖的順序是到達(dá)的先后順序晰骑;在NoneFairSync中,writerShouldBlock()方法始終返回false绊序,表示在非公平鎖中硕舆,只要一個(gè)線程嘗試競(jìng)爭(zhēng)鎖的執(zhí)行權(quán)限,那么其就會(huì)不會(huì)被阻塞政模。這也就是公平鎖和非公平鎖的區(qū)別岗宣。
???????類似于tryAcquire(int)方法,tryAcquireShared(int)方法則表示當(dāng)前線程能否以共享的方式獲取鎖的執(zhí)行權(quán)限淋样。如下是該方法的代碼:
protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
// 判斷是否有寫鎖存在耗式,或者是有寫鎖存在的情況下是否占用寫鎖的是當(dāng)前線程
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c);
// 判斷讀鎖是否應(yīng)該被阻塞,其次判斷占用讀鎖的次數(shù)是否超過最大限制趁猴,
// 并且嘗試更新CAS算法設(shè)置state屬性的值為新的值
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current; // 記錄第一個(gè)獲取讀鎖的線程
firstReaderHoldCount = 1; // 記錄第一個(gè)獲取讀鎖的線程重入次數(shù)
} else if (firstReader == current) {
firstReaderHoldCount++; // 更新第一個(gè)獲取讀鎖的線程的重入次數(shù)
} else {
HoldCounter rh = cachedHoldCounter; // 獲取緩存的線程的計(jì)數(shù)器
// 如果緩存的計(jì)數(shù)器不為當(dāng)前線程的計(jì)數(shù)器不為當(dāng)前線程的計(jì)數(shù)器刊咳,則更新其為當(dāng)前線程的計(jì)數(shù)器
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0) // 如果當(dāng)前線程計(jì)數(shù)器計(jì)數(shù)為0,則為其設(shè)置一個(gè)計(jì)數(shù)器
readHolds.set(rh);
rh.count++; // 更新計(jì)數(shù)器的值儡司,即當(dāng)前線程的重入次數(shù)
}
return 1;
}
return fullTryAcquireShared(current);
}
???????從tryAcquireShared(int)方法可以看出娱挨,其首先判斷是否有線程占用寫鎖,并且如果有線程占用寫鎖捕犬,則判斷占用寫鎖的線程是否不為當(dāng)前線程跷坝,如果都不成立,則說明有其他的線程占用寫鎖碉碉,那么當(dāng)前線程就會(huì)被阻塞柴钻。這也就是讀寫鎖中鎖降級(jí)的原理,即一個(gè)鎖獲取讀鎖之后垢粮,在還沒有釋放鎖的情況下獲取寫鎖贴届,然后釋放讀鎖,這樣該線程就只占用一個(gè)讀鎖了,即從寫鎖降級(jí)為讀鎖毫蚓。這里的判斷當(dāng)前占用寫鎖的線程是否為其他線程就保證了當(dāng)前線程如果占用了寫鎖占键,那么其可以同時(shí)嘗試獲取讀鎖。在接下來的if條件中元潘,首先判斷readerShouldBlock()畔乙,即當(dāng)前讀線程是否應(yīng)該被阻塞,該方法也是FairSync和NoneFairSync中重寫的方法翩概,在FairSync中啸澡,其會(huì)判斷阻塞隊(duì)列中是否有阻塞的線程,如果有則當(dāng)前線程也應(yīng)該被阻塞氮帐,在NoneFairSync中,其會(huì)判斷當(dāng)前阻塞隊(duì)列中最近的被阻塞的節(jié)點(diǎn)是否為嘗試獲取寫鎖的線程洛姑,如果是上沐,則當(dāng)前線程應(yīng)該被阻塞。如果讀鎖不應(yīng)該被阻塞楞艾,并且當(dāng)前線程不會(huì)造成獲取讀鎖次數(shù)超限参咙,那么就會(huì)嘗試通過CAS算法更新state的值為新的值。在成功通過CAS算法設(shè)置state屬性值之后硫眯,說明當(dāng)前線程成功獲取到了讀鎖蕴侧,那么其就會(huì)更新記錄其重入次數(shù)的計(jì)數(shù)器。如果上述條件不成立两入,那么當(dāng)前線程就會(huì)通過無線for循環(huán)嘗試獲取鎖的權(quán)限净宵,具體的實(shí)現(xiàn)在fullTryAcquireShared(Thread)中:
final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;;) {
int c = getState();
if (exclusiveCount(c) != 0) { // 判斷是否有寫鎖正在被占用
if (getExclusiveOwnerThread() != current) // 判斷占用寫鎖的是否為當(dāng)前線程
return -1;
} else if (readerShouldBlock()) { // 判斷讀鎖是否應(yīng)該被阻塞
if (firstReader == current) {
} else { // 走到這一步說明沒有寫鎖占用,或者是占用寫鎖的線程為當(dāng)前線程裹纳,并且讀鎖不應(yīng)該被阻塞择葡,
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0) // 如果重入次數(shù)為0,說明其當(dāng)前是沒有占用讀鎖的狀態(tài)
readHolds.remove();
}
}
if (rh.count == 0) // 當(dāng)前線程沒有占用讀鎖剃氧,因?yàn)樘幱趓eaderShouldBlock()塊中敏储,因而應(yīng)該被阻塞
return -1;
}
}
if (sharedCount(c) == MAX_COUNT)
throw new Error("Maximum lock count exceeded");
if (compareAndSetState(c, c + SHARED_UNIT)) { // 使用CAS算法更新state屬性的值
if (sharedCount(c) == 0) {
firstReader = current; // 記錄第一個(gè)讀線程
firstReaderHoldCount = 1; // 記錄第一個(gè)讀線程的重入次數(shù)
} else if (firstReader == current) {
firstReaderHoldCount++; // 更新第一個(gè)讀線程的重入次數(shù)
} 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++; // 更新當(dāng)前線程的重入次數(shù)
cachedHoldCounter = rh; // 緩存當(dāng)前讀線程的計(jì)數(shù)器
}
return 1;
}
}
}
???????可以看出tryAcquireShared(int)和fullTryAcquireShared(Thread)方法的區(qū)別在于tryAcquireShared(int)方法只會(huì)對(duì)當(dāng)前線程嘗試一次是否能夠獲取到讀鎖的權(quán)限,其不一定能夠保證當(dāng)前線程獲取到讀鎖朋鞍,而fullTryAcquireShared(Thread)方法則在一個(gè)無限for循環(huán)中嘗試獲取讀鎖已添,其能夠保證在沒有寫鎖或者寫鎖不為當(dāng)前鎖的情況下當(dāng)前線程一定能夠獲取到讀鎖。這里也可以看出滥酥,在不存在寫鎖的情況下更舞,如果是多個(gè)線程嘗試競(jìng)爭(zhēng)讀鎖,那么每個(gè)線程都會(huì)進(jìn)入無限for循環(huán)中恨狈,并且保證一定能夠獲取到讀鎖疏哗,此時(shí)CPU是一直在運(yùn)轉(zhuǎn)的,相較于AbstractQueuedSynchronizer.doAcquireShared(int)方法,如果當(dāng)前線程沒有競(jìng)爭(zhēng)到讀鎖返奉,那么其也會(huì)進(jìn)入無限for循環(huán)贝搁,但是在循環(huán)一次之后其就會(huì)被操作系統(tǒng)“擱置”起來,從而釋放資源芽偏。ReentrantReadWriteLock的這種競(jìng)爭(zhēng)讀鎖的機(jī)制能夠保證線程以最快的方式獲取到讀鎖雷逆,而不必被“擱置”,從而造成不必要的線程環(huán)境切換污尉。
???????上面講解的tryAcquire(int)和tryAcquireShared(int)方法分別被獲取寫鎖的線程和獲取讀鎖的線程調(diào)用膀哲,分別表示能否成功以獨(dú)占的方式和以共享的方式獲取寫鎖和讀鎖的權(quán)限。接下來我們來看看tryRelease(int)和tryReleaseShared(int)方法被碗,這兩個(gè)方法分別表示能否成功以獨(dú)占的方式和以共享的方式釋放寫鎖和讀鎖的權(quán)限某宪。如下是tryRelease(int)方法的源碼:
protected final boolean tryRelease(int releases) {
if (!isHeldExclusively()) // 判斷當(dāng)前線程是否為以獨(dú)占方式占用寫鎖權(quán)限的線程
throw new IllegalMonitorStateException();
int nextc = getState() - releases; // 記錄釋放寫鎖后state屬性的值
boolean free = exclusiveCount(nextc) == 0; // 判斷釋放寫鎖后是否還存在寫鎖
if (free)
setExclusiveOwnerThread(null); // 如果不存在寫鎖,則清楚獨(dú)占鎖的線程狀態(tài)
setState(nextc); // 設(shè)置state屬性的值
return free;
}
???????可以看到锐朴,在tryRelease(int)方法中兴喂,其首先判斷當(dāng)前線程是否為以獨(dú)占方式占用(寫)鎖的線程,如果不是則其沒有權(quán)限釋放鎖焚志。接著主要是對(duì)state屬性的值進(jìn)行相應(yīng)的更新衣迷。這里需要注意的是,在free變量的判斷中酱酬,使用的是exclusiveCount(nextc)是否為0壶谒,其實(shí)可以理解,當(dāng)前以獨(dú)占方式占用寫鎖的線程膳沽,如果其沒有同時(shí)占用讀鎖汗菜,那么其高位部分肯定都是0,因而只需要判斷nextc是否為0即可挑社,這里去除高位部分呵俏,只判斷低位部分是否為0的原因就是當(dāng)前線程可能既同時(shí)占用寫鎖又占用了讀鎖,因而不能簡(jiǎn)簡(jiǎn)單單的判斷nextc是否為0滔灶。接下來我們看看tryReleaseShared(int)方法普碎,以下是該方法的實(shí)現(xiàn):
protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) { // 如果當(dāng)前線程為第一個(gè)更新state屬性的線程,則維護(hù)相關(guān)的變量值
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount--;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) // 獲取當(dāng)前線程的計(jì)數(shù)器
rh = readHolds.get();
int count = rh.count;
if (count <= 1) { // 如果計(jì)數(shù)器的計(jì)數(shù)值小于等于1录平,說明釋放后其會(huì)至少變?yōu)?麻车,因而清除計(jì)數(shù)器相關(guān)信息
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
--rh.count; // 更新計(jì)數(shù)器的值
}
for (;;) {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc)) // 以CAS算法更新state屬性的值
return nextc == 0;
}
}
???????可以看到,tryReleaseShared(int)方法首先判斷當(dāng)前線程是否為將state屬性從0更新為1的線程斗这,如果是动猬,則更新相關(guān)的屬性值,否則對(duì)當(dāng)前線程的計(jì)數(shù)器信息進(jìn)行更新表箭。更新完相關(guān)屬性值之后赁咙,當(dāng)前線程會(huì)進(jìn)入一個(gè)無線for循環(huán),并且以CAS算法更新state屬性的值,這樣可以保證一定能夠以原子的形式將state更新成功彼水。這里返回值為nextc是否為0崔拥,這是因?yàn)楫?dāng)前線程可能重入當(dāng)前鎖多次,也就是說如果當(dāng)前線程還沒有完全釋放當(dāng)前鎖的時(shí)候凤覆,其是不會(huì)執(zhí)行后續(xù)喚醒后續(xù)線程中的代碼的链瓦。