前面分析了AbstractQueuedSynchronizer實(shí)現(xiàn)的其他兩部分:
Condition源碼解析
獨(dú)占模式解析
本文繼續(xù)介紹AbstractQueuedSynchronizer最后一部分功能--共享模式
共享模式資源獲取
共享模式獲取資源的入口如下
// 忽略中斷異常
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 拋出中斷異常
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// 超時(shí)獲取,并拋出中斷異常
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
tryAcquireShared留給工具自己去實(shí)現(xiàn)睬辐,用于判斷是否滿足獲取資源要求儡率,與獨(dú)占模式判斷函數(shù)不一樣,tryAcquire返回的是boolean食寡,因?yàn)槭仟?dú)占模式熄捍,每次只能一個(gè)線程獲取資源烛恤,所以直接返回boolean,共享模式下資源可以被多個(gè)線程通知占用余耽,tryAcquireShared返回int類型缚柏,表示還有多少個(gè)資源可以同時(shí)被占用,用于共享模式下傳播喚醒碟贾。
doAcquireShared
重點(diǎn)分析下doAcquireShared方法币喧,doAcquireSharedInterruptibly和doAcquireSharedNanos區(qū)別不是很大,不做分析
private void doAcquireShared(int arg) {
//添加共享模式節(jié)點(diǎn)袱耽,主要區(qū)分獨(dú)占模式
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//拿到當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)杀餐,如果前驅(qū)節(jié)點(diǎn)是head節(jié)點(diǎn),說明沒有等待節(jié)點(diǎn)
final Node p = node.predecessor();
if (p == head) {
// 嘗試獲取資源扛邑,大于等于0怜浅,說明有資源獲取。
int r = tryAcquireShared(arg);
if (r >= 0) {
//把當(dāng)前節(jié)點(diǎn)設(shè)置成head節(jié)點(diǎn)蔬崩,并傳播喚醒后面的節(jié)點(diǎn)恶座。
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// 這里和獨(dú)占模式一樣,如果沒資源申請(qǐng)沥阳,封裝節(jié)點(diǎn)跨琳,并park等待
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate
private void setHeadAndPropagate(Node node, int propagate) {
// 設(shè)置head節(jié)點(diǎn),保留之前的head的節(jié)點(diǎn)桐罕,用于是否傳播喚醒之后節(jié)點(diǎn)判斷
Node h = head; // Record old head for check below
setHead(node);
/**
* 需要傳播喚醒的幾個(gè)條件
* 1脉让,propagate>0,當(dāng)大于0的時(shí)候,說明還有其他資源空余功炮,需要傳播喚醒之后的節(jié)點(diǎn)
* 2溅潜,head == null || head.waitStatus < 0,頭結(jié)點(diǎn)為空薪伏,
head.waitStatus < 0即需要喚醒狀態(tài)或者是傳播狀態(tài)滚澜,也無條件嘗試喚醒之后的節(jié)點(diǎn)
* 喚醒之后的節(jié)點(diǎn),會(huì)去重新嘗試獲取資源嫁怀。
* 這里存在誤喚醒设捐,不過沒關(guān)系,喚醒之后的節(jié)點(diǎn)塘淑,會(huì)繼續(xù)回到doAcquireShared for循環(huán)中萝招,嘗試獲取資源。
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 判斷是否是共享節(jié)點(diǎn)
if (s == null || s.isShared())
doReleaseShared();
}
}
以上是共享模式獲取資源流程存捺,釋放資源函數(shù)如下:
doReleaseShared
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
// 如果head節(jié)點(diǎn)的后繼節(jié)點(diǎn)需要被喚醒槐沼,然后喚醒
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
// 如果head節(jié)點(diǎn)后沒有需要被喚醒的節(jié)點(diǎn),設(shè)置成PROPAGATE狀態(tài),然后傳播喚醒
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}