本文基于java version "1.8.0_77"
閱讀本文章之前,你需要了解LockSupport中相關(guān)方法的介紹。
閱讀本篇文章,請對照源碼閱讀,否則可能云里霧里不知所云。
簡介
AbstractQueuedSynchronizer:譯為:隊(duì)列同步器(以下簡稱AQS),可以看到這是一個抽象類。有大名鼎鼎的并發(fā)大師Doug Lea設(shè)計:
并發(fā)包中很多Lock都是通過繼承AQS實(shí)現(xiàn)的(ReentrantLock、
ReentrantReadWriteLock和CountDownLatch等),AQS中封裝了實(shí)現(xiàn)鎖的具體操作,其子類繼承AQS后,可以輕松的調(diào)用AQS的相應(yīng)方法來實(shí)現(xiàn)同步狀態(tài)的管理同步狀態(tài),線程的排隊(duì),等待以及喚醒等操作。
子類可以重寫的方法如下:
-
protected boolean tryAcquire(int arg)
獨(dú)占式的獲取同步狀態(tài),使用CAS設(shè)置同步狀態(tài) -
protected boolean tryRelease(int arg)
獨(dú)占式的釋放同步狀態(tài) -
protected int tryAcquireShared(int arg)
共享式的獲取同步狀態(tài),返回大于等于0的值,表示獲取成功,否則失敗 -
protected boolean tryReleaseShared(int arg)
共享式的釋放同步狀態(tài) -
protected boolean isHeldExclusively()
判斷當(dāng)前是否被當(dāng)前線程鎖獨(dú)占
構(gòu)成
如上圖,AQS中定義了一個volatile整數(shù)狀態(tài)信息,我們可以通過getState()
,setState(int newState)
,compareAndSetState(int expect,int update))
等protected方法進(jìn)行操作這一狀態(tài)信息。例如:ReentrantLock中用它來表示所有線程呢個已經(jīng)重復(fù)獲取該鎖的次數(shù),Semaphore用它來表示剩余的許可數(shù)量,F(xiàn)utureTask用它來表示任務(wù)的狀態(tài)(未開始,正在運(yùn)行,已結(jié)束,已取消等)。
AQS是由一個同步隊(duì)列(FIFO雙向隊(duì)列)來管理同步狀態(tài)的,如果線程獲取同步狀態(tài)失敗,AQS會將當(dāng)前線程以及等待狀態(tài)信息構(gòu)造成一個節(jié)點(diǎn)(Node)加入到同步隊(duì)列中,同時阻塞當(dāng)前線程;當(dāng)同步狀態(tài)狀態(tài)釋放時,會把首節(jié)點(diǎn)中的線程喚醒,使其再次嘗試獲取同步狀態(tài)。
準(zhǔn)備工作
在跟著源碼走流程之前,我們先了一下以下幾個需要用到的概念:
AQS.Node
隊(duì)列示意圖如下:
每個Node節(jié)點(diǎn)都是一個自旋鎖
:在阻塞時不斷循環(huán)讀取狀態(tài)變量,當(dāng)前驅(qū)節(jié)點(diǎn)釋放同步對象使用權(quán)后,跳出循環(huán),執(zhí)行同步代碼。我們在接下來的代碼分析中,也能夠看到通過死循環(huán)來達(dá)到自旋這一目的。
我們看一下Node節(jié)點(diǎn)類的幾個關(guān)鍵屬性(不必記住,下面用到的時候,再回來看即可):
MODE(兩個)
兩種Mode,用于創(chuàng)建Node時的構(gòu)造函數(shù)使用。在
private Node addWaiter(Node mode)
這一方法調(diào)用的時候傳入,用于想等待隊(duì)列中添加節(jié)點(diǎn)。
volatile int waitStatus
手機(jī)是waitStatus
,用來表示當(dāng)前節(jié)點(diǎn)的狀態(tài)。其取值范圍如下:
static final int CANCELLED = 1
;表示節(jié)點(diǎn)的線程是已被取消的。當(dāng)前節(jié)點(diǎn)由于超時或者被中斷而被取消。一旦節(jié)點(diǎn)被取消后,那么它的狀態(tài)值不在會被改變,且當(dāng)前節(jié)點(diǎn)的線程不會再次被阻塞。static final int SIGNAL= -1
;表示當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)的線程需要被喚醒。當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)
已經(jīng) (或即將)被阻塞(通過LockSupport.park()) , 所以當(dāng) 當(dāng)前節(jié)點(diǎn)釋放或則被取消時候,一定要unpark它的后繼節(jié)點(diǎn)。為了避免競爭,獲取方法一定要首先設(shè)置node為signal,然后再次重新調(diào)用獲取方法,如果失敗,則阻塞。static final int CONDITION = -2;
表示線程正在等待某個條件。表示當(dāng)前節(jié)點(diǎn)正在條件隊(duì)列(AQS下的ConditionObject里也維護(hù)了個隊(duì)列)中,在從conditionObject隊(duì)列轉(zhuǎn)移到同步隊(duì)列前,它不會在同步隊(duì)列(AQS下的隊(duì)列)中被使用。當(dāng)成功轉(zhuǎn)移后,該節(jié)點(diǎn)的狀態(tài)值將由CONDITION設(shè)置為0。static final int PROPAGATE = -3;
表示下一個共享模式的節(jié)點(diǎn)應(yīng)該無條件的傳播下去。共享模式下的釋放操作應(yīng)該被傳播到其他節(jié)點(diǎn)。該狀態(tài)值在doReleaseShared方法中被設(shè)置的。0
以上都不是。
可以看到,非負(fù)數(shù)值(0和已經(jīng)取消)意味著該節(jié)點(diǎn)不需要被喚醒。所以,大多數(shù)代碼中不需要檢查該狀態(tài)值的確定值,只需要根據(jù)正負(fù)值來判斷即可對于一個正常的Node,他的waitStatus初始化值時0。對于一個condition隊(duì)列中的Node,他的初始化值時CONDITION。如果想要修改這個值,可以使用AQS提供CAS進(jìn)行修改。(方法:boolean compareAndSetWaitStatus(Node node, int expect,int update)
)
volatile Node prev
用于鏈接當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn),當(dāng)前節(jié)點(diǎn)依賴前驅(qū)節(jié)點(diǎn)來檢測waitStatus,前驅(qū)節(jié)點(diǎn)是在當(dāng)前節(jié)點(diǎn)入隊(duì)時候被設(shè)置的。為了提高GC效率,在當(dāng)前節(jié)點(diǎn)出隊(duì)時候會把前驅(qū)節(jié)點(diǎn)設(shè)置為null。而且,在取消前驅(qū)節(jié)點(diǎn)的時候,則會while循環(huán)直到找到一個非取消(cancelled)的節(jié)點(diǎn),由于頭節(jié)點(diǎn)永遠(yuǎn)不會是取消狀態(tài),所以我們一定可以找到非取消狀態(tài)的前置節(jié)點(diǎn)。
volatile Node next;
用于鏈接當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn),在當(dāng)前節(jié)點(diǎn)釋放時候會喚醒后繼節(jié)點(diǎn)。在一個當(dāng)前節(jié)點(diǎn)入隊(duì)的時候,會先設(shè)置當(dāng)前節(jié)點(diǎn)的prev,而不會立即設(shè)置前置節(jié)點(diǎn)的next。而是用CAS替換了tail之后才設(shè)置前置節(jié)點(diǎn)的next。(方法Node addWaiter(Node mode))
Node nextWaiter
用來串聯(lián)條件隊(duì)列,連接到下一個在條件上等待的結(jié)點(diǎn)或是特殊的值SHARED。因?yàn)闂l件隊(duì)列只在獨(dú)占模式下持有時訪問,我們只需要一個簡單的鏈表隊(duì)列來持有在條件上等待的結(jié)點(diǎn)。再然后它們會被轉(zhuǎn)移到同步隊(duì)列(AQS隊(duì)列)再次重新獲取。由于條件隊(duì)列只能在獨(dú)占模式下使用,所以我們要表示共享模式的節(jié)點(diǎn)的話只要使用特殊值SHARED來標(biāo)明即可。
輔助方法分析(供查閱)
shouldParkAfterFailedAcquire
這個方法是信號控制(waitStatus)的核心。在獲取同步狀態(tài)失敗,生成Node并加入隊(duì)列中后,用于檢查和更新結(jié)點(diǎn)的狀態(tài)。返回true表示當(dāng)前節(jié)點(diǎn)應(yīng)該被阻塞。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* 前驅(qū)節(jié)點(diǎn)如果狀態(tài)如果為SIGNAL。表明當(dāng)前節(jié)點(diǎn)應(yīng)被阻塞,等待喚醒(參見上文的SIGNAL狀態(tài))
* 則返回true,然后park掛起線程
*/
return true;
if (ws > 0) {
/*
* 前驅(qū)節(jié)點(diǎn)狀態(tài)值大于0(只有一個取值1),表示前驅(qū)節(jié)點(diǎn)已經(jīng)取消
* 此時應(yīng)該丟棄前驅(qū)節(jié)點(diǎn),而繼續(xù)尋找前驅(qū)節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn),(見下圖)
* 這里使用while循環(huán)查找前驅(qū)節(jié)點(diǎn),并將當(dāng)前節(jié)點(diǎn)的prev屬性設(shè)置為找到的新的節(jié)點(diǎn)。(下圖步驟1)
* 并將新的前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)(下圖步驟2)
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* 排除以上SIGNAL(-1)和>0(1)兩種情況
* 現(xiàn)在是前驅(qū)節(jié)點(diǎn)的waitStatus為0或PROPAGATE(-3)的情況(不考慮CONDITION的情況)
* 這時候表明前驅(qū)節(jié)點(diǎn)需要重新設(shè)置waitStatus
* 這樣在下一輪循環(huán)中,就可以判斷前驅(qū)節(jié)點(diǎn)的SIGNAL而阻塞park當(dāng)前節(jié)點(diǎn),以便于等待前驅(qū)節(jié)點(diǎn)的unpark(比如:shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
如圖:
parkAndCheckInterrupt
與上面的shouldParkAfterFailedAcquire
中聯(lián)合調(diào)用
(shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
通過shouldParkAfterFailedAcquire
方法獲取到可用的前驅(qū)節(jié)點(diǎn),并設(shè)置前驅(qū)節(jié)點(diǎn)的WaitStatus值為SIGNAL,進(jìn)而在此方法中將當(dāng)前線程park(阻塞等待)。線程醒了之后,檢查線程是否被重點(diǎn),并將結(jié)果返回。
cancelAcquire
上面講到,每一個NODE節(jié)點(diǎn)都是一個自旋鎖,都在不斷進(jìn)行死循環(huán)自旋,當(dāng)自旋過程中發(fā)生異常而無法獲得鎖,就需要取消節(jié)點(diǎn)。
需要做的是:
- 清空node節(jié)點(diǎn)中的引用
- node出隊(duì):剔除當(dāng)前節(jié)點(diǎn),打斷next和prev引用。分為三種情況:1. node是tail 2. node既不是tail,也不是head的后繼節(jié)點(diǎn) 3. node是head的后繼節(jié)點(diǎn)
源碼分析如下:
private void cancelAcquire(Node node) {
// 如果node為空,忽略,直接返回
if (node == null)
return;
//將thread引用置空
node.thread = null;
// 跳過取消的(cancelled)的前置節(jié)點(diǎn),找到一個有效的前驅(qū)節(jié)點(diǎn),如上面分析過的shouldParkAfterFailedAcquire
Node pred = node.prev;
while (pred.waitStatus > 0)
node.prev = pred = pred.prev;
// 拿到前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn)
Node predNext = pred.next;
// 將節(jié)點(diǎn)的狀態(tài)值設(shè)為已取消,這樣,其他節(jié)點(diǎn)就可以跳過本節(jié)點(diǎn),而不受其他線程的干擾
node.waitStatus = Node.CANCELLED;
// 情況1:如果當(dāng)前節(jié)點(diǎn)是尾節(jié)點(diǎn),CAS替換tail字段的引用為為前驅(qū)節(jié)點(diǎn)
// 成功之后,CAS將前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn)置空
if (node == tail && compareAndSetTail(node, pred)) {
compareAndSetNext(pred, predNext, null);
} else {
// 情況2:如果當(dāng)前節(jié)點(diǎn)不是tail,而前驅(qū)節(jié)點(diǎn)又不是head
// 則嘗試CAS將前驅(qū)節(jié)點(diǎn)的waitStatus標(biāo)記為SIGNAL(表示前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn)需要喚醒)
// 設(shè)置成功之后,CAS將前驅(qū)節(jié)點(diǎn)的后繼節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)(將當(dāng)前節(jié)點(diǎn)剔除)
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
(ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
compareAndSetNext(pred, predNext, next);
} else {
// 情況3:如果node是head的后繼節(jié)點(diǎn),則直接喚醒node的后繼節(jié)點(diǎn)
unparkSuccessor(node);
}
node.next = node; // help GC
}
}
如上:
情況1:
- 1:compareAndSetTail(node, pred) 替換tail的引用
- 2:compareAndSetNext(pred, predNext, null); 將pred的next置空
情況2:
- compareAndSetNext(pred, predNext, next); 將前驅(qū)節(jié)點(diǎn)的next指向后繼節(jié)點(diǎn)。后繼節(jié)點(diǎn)的prev將在前面講過的shouldParkAfterFailedAcquire進(jìn)行添加。
情況3
下面將分析unparkSuccessor方法
unparkSuccessor
用于喚醒當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)。
private void unparkSuccessor(Node node) {
// 將當(dāng)前節(jié)點(diǎn)的狀態(tài)重置
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
// 拿到后繼節(jié)點(diǎn) ,如果后繼機(jī)節(jié)點(diǎn)是空或標(biāo)記為取消(cancelled)
// 開是循環(huán)獲取后繼的可用節(jié)點(diǎn)
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
// LockSupport喚醒下一個節(jié)點(diǎn)
if (s != null)
LockSupport.unpark(s.thread);
}
上文中尋找下一個可用節(jié)點(diǎn)的時候,可以看到不是head->tail尋找,而是tail->head倒序?qū)ふ遥@是因?yàn)椋和ㄟ^上面代碼可以看到,只有在當(dāng)前節(jié)點(diǎn)node的后繼節(jié)點(diǎn)為nul的時候,才會執(zhí)行循環(huán)尋找后面的可用后繼節(jié)點(diǎn)。注意此處:后繼節(jié)點(diǎn)已經(jīng)為null了
,故只能從尾部向前遍歷,找到第一個可用節(jié)點(diǎn)。
差不多就這些了,下面我們進(jìn)入正題,探討一下獲取同步化狀態(tài)的流程。
-----------------------------------------------------
源碼分析
獨(dú)占式獲取同步狀態(tài)
上源碼:
首先tryAcquire(arg),tryAcquire是由子類實(shí)現(xiàn),通過操作state進(jìn)行判定當(dāng)前是否允許當(dāng)前線程獲取執(zhí)行權(quán)力,用來控制當(dāng)前是否允許獲取同步狀態(tài)。true表示獲取同步狀態(tài),不必加入同步隊(duì)列中。如果返回了false,沒有獲取同步狀態(tài),則需要加入到同步隊(duì)列中。繼續(xù)往下執(zhí)行:
addWaiter(Node mode)
首先將節(jié)點(diǎn)添加到等待隊(duì)列中:
private Node addWaiter(Node mode) {
// 構(gòu)造一個Node,nextWaiter為null
Node node = new Node(Thread.currentThread(), mode);
// 獲取到tail節(jié)點(diǎn)(也就是接下來,當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn))
Node pred = tail;
if (pred != null) {
node.prev = pred;
// CAS嘗試替換tail引用,如果成功,則返回
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
// 上述不成功,存在多線程競爭,則自旋
enq(node);
return node;
}
enq(final Node node)
private Node enq(final Node node) {
for (;;) {
Node t = tail;
// 如果隊(duì)列為空,先CAS設(shè)置一下head空節(jié)點(diǎn),完事之后進(jìn)行下一次循環(huán)
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
// 設(shè)置當(dāng)前節(jié)點(diǎn)的prev,然后CAS設(shè)置設(shè)置tail,和前驅(qū)節(jié)點(diǎn)的next
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
添加隊(duì)列成功之后,我們繼續(xù)往下看,還是那張圖
acquireQueued(final Node node, int arg)
acquireQueued主要是處理正在排隊(duì)等待的線程。自旋、阻塞重試獲取。如果獲取成功則替換當(dāng)前節(jié)點(diǎn)為鏈表頭,然后返回。在獲取過程中,忽略了中斷,但將是否中斷的返回了。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
// 死循環(huán)自旋,不斷嘗試獲取同步狀態(tài)
for (;;) {
//獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)
final Node p = node.predecessor();
// 只有前驅(qū)節(jié)點(diǎn)是head,也就是說排隊(duì)排到當(dāng)前借錢,才有可能獲取同步狀態(tài)
// 如果允許獲取同步狀態(tài),則將當(dāng)前節(jié)點(diǎn)設(shè)置為head,設(shè)置其他標(biāo)記,并返回,終止自旋
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
// 在上面同步獲取失敗后,有可能不是頭節(jié)點(diǎn)的后繼節(jié)點(diǎn),這時沒有資格獲取同步狀態(tài),就需要休眠
// 下面代碼上面講過,進(jìn)一步檢查和更新節(jié)點(diǎn)狀態(tài),判斷當(dāng)前節(jié)點(diǎn)是否需要park,減少占用CPU,等待前驅(qū)節(jié)點(diǎn)釋放同步狀態(tài)將它喚醒
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 如果失敗,取消獲取同步狀態(tài),移除節(jié)點(diǎn),上文已講
if (failed)
cancelAcquire(node);
}
}
selfInterrupt
獲取鎖過程中,忽略了中斷,在這里處理中斷
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
獲取分析完了,我們看一下,同步代碼執(zhí)行完畢,同步狀態(tài)是如何釋放的。
獨(dú)占式釋放同步狀態(tài)
public final boolean release(int arg) {
//首先調(diào)用子類重寫方法tryRelease,返回true標(biāo)識標(biāo)識允許釋放同步狀態(tài)
if (tryRelease(arg)) {
//如果允許釋放,則當(dāng)前head即為要釋放的node,只需要喚醒后繼node即可, unparkSuccessor上文講過
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
到此,我們走完了獨(dú)占式鎖的獲取與釋放。簡要概述一下步驟:
- 嘗試獲取鎖,如果不能獲取,添加進(jìn)隊(duì)列
- 隊(duì)列中該node進(jìn)行自旋排隊(duì),嘗試獲取同步狀態(tài)
- 如果當(dāng)前節(jié)點(diǎn)不是head的下個節(jié)點(diǎn),休眠,等待喚醒
- 喚醒后,檢查自身是否已被interrupted,繼續(xù)嘗試獲取鎖
- 獲取后,執(zhí)行同步代碼,
- 執(zhí)行完畢后,release鎖,喚醒下個節(jié)點(diǎn)
-----------------------------------------------------
共享式獲取同步狀態(tài)
上源碼:
首先還是調(diào)用子類實(shí)現(xiàn)的tryAcquireShared,查看是否允許獲取同步狀態(tài)。如果首次獲取結(jié)果大于等于0.則完成獲取 。如果小于0,則表示不允許獲取同步狀態(tài),進(jìn)入隊(duì)列。
doAcquireShared(int arg)
死循環(huán)自旋嘗試獲取鎖
private void doAcquireShared(int arg) {
// 構(gòu)造Node,添加到隊(duì)列中,模式為Node.SHARED,查看Node構(gòu)造函數(shù)
// 可以看到,當(dāng)前Node的nextWaiter(不是next,詳看上文)為一個空node對象
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 拿到前驅(qū)node
final Node p = node.predecessor();
// 前驅(qū)node是head才有可能獲取鎖
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) { // tryAcquireShared大于等于0,允許獲取鎖
// 獲取成功,需要將當(dāng)前節(jié)點(diǎn)設(shè)置為AQS隊(duì)列中的第一個節(jié)點(diǎn)
// 這是AQS的規(guī)則,隊(duì)列的頭節(jié)點(diǎn)表示正在獲取鎖的節(jié)點(diǎn)
// 下面講解
setHeadAndPropagate(node, r);
p.next = null; // help GC
// 同獨(dú)占式
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// 不解釋,見上文
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
// 不解釋,見上文
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate
這是
private void setHeadAndPropagate(Node node, int propagate) {
// 取到head做緩存
Node h = head;
//將當(dāng)前節(jié)點(diǎn)設(shè)置為head
setHead(node);
// propagate是tryAcquireShared返回的值 ,可以理解為Semaphore,是否還允許其他并發(fā)
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 并檢查當(dāng)前節(jié)點(diǎn)的后繼節(jié)點(diǎn)為空或者后繼節(jié)點(diǎn)的nextWaiter是否為SHARED,表明后繼節(jié)點(diǎn)需要共享傳遞
if (s == null || s.isShared())
doReleaseShared(); // 進(jìn)行share傳遞, doReleaseShared
}
}
可以看到這里與獨(dú)占式的做了相似的事情,都進(jìn)行了設(shè)置head之后,區(qū)別是共享式獲取同步狀態(tài)又進(jìn)行了share傳遞,傳遞給下一個nextWaiter屬性同樣為SHAREED的節(jié)點(diǎn),我們看一下doReleaseShared方法
doReleaseShared
private void doReleaseShared() {
/*
* 即使在并發(fā),多個線程在獲取、釋放的情況下,確保釋放的傳播性,
* 如果當(dāng)前節(jié)點(diǎn)標(biāo)記為SIGNAL(表示后繼節(jié)點(diǎn)需要喚醒,按理說應(yīng)該在當(dāng)前節(jié)點(diǎn)釋放的時候喚醒,但是此處是共享模式,故立即喚醒),則通常嘗試頭節(jié)點(diǎn)的unparkSuccessor 動作。
* 但是如果他不符合喚醒的條件,為了確保能正確release,那么則把head的waitState設(shè)置為為PROPAGATE
* 此外,在執(zhí)行該代碼時,為了以防萬一有新
* 節(jié)點(diǎn)的加入,或者我們CAS修改失敗,所以我們的更新需要在循環(huán)中,不斷嘗試。
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // 失敗了就繼續(xù)loop
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // 失敗了就繼續(xù)loop
}
if (h == head) // loop if head changed
break;
}
}
這里最重要的是要多線程環(huán)境中理解doReleaseShared,一個線程A執(zhí)行doReleaseShared,然后unparkSuccessor,線程B喚醒執(zhí)行,這時候被喚醒的線程B運(yùn)行,重新請求獲取同步狀態(tài),修改head節(jié)點(diǎn),喚醒線程C,然后依次喚醒D、E、F……每個節(jié)點(diǎn)在自己喚醒的同時,也喚醒了后面的節(jié)點(diǎn),設(shè)置為head,這樣就達(dá)到了共享模式。
注意h == head,我們看到上面有注釋說Additionally, we must loop in case a new node is added while we are doing this.
為了避免在執(zhí)行到這里的時候。如果有兩個新的節(jié)點(diǎn)添加到隊(duì)列中來,一個節(jié)點(diǎn)A喚醒B之后,B恰好setHead了,此時head是B節(jié)點(diǎn)。此時A之前獲得的head并不是新的head了,故需要繼續(xù)循環(huán),以盡可能保證成功性。
可以看到 獨(dú)占式與共享式的差別就是共享的傳遞:
獨(dú)占模式喚醒頭節(jié)點(diǎn),頭節(jié)點(diǎn)釋放之后,后繼節(jié)點(diǎn)喚醒
共享模式喚醒全部節(jié)點(diǎn)。
共享式釋放同步狀態(tài)
源碼不貼了,調(diào)用的是上述的doReleaseShared()
響應(yīng)中斷獲取鎖
acquireInterruptibly和acquire差不多,acquireSharedInterruptibly和acquireShared差不多,區(qū)別就是拋出了InterruptedException。
-----------------------完畢-------------------------
下一篇繼續(xù)擼Reentrantlock
本人能力有限,分析的不夠的地方,還望多多指正。