一、什么是AQS
JAVA中痕貌,在多線程環(huán)境下风罩,一般需要鎖來確保數(shù)據(jù)安全,而我們通常所說的鎖舵稠,其實(shí)分為了兩類超升,一類是sychronized,利用的是指令級別的monitor-enter 和 monitor-exit哺徊;另一類是Lock室琢,例如ReentrantLock等,是依靠代碼實(shí)現(xiàn)的落追。
而AQS盈滴,即AbstractQueuedSynchronizer,又叫隊(duì)列同步器淋硝,就是用來構(gòu)建鎖和其他同步組件的基礎(chǔ)框架雹熬。
AQS是一個(gè)以繼承方式使用的抽象類,他維護(hù)了一個(gè)volatile的state和一個(gè)FIFO線程等待隊(duì)列谣膳,類們必須定義改變state變量的protected方法竿报,這些方法定義了state是如何被獲取或釋放的。
二继谚、AQS原理簡介
2.1 status狀態(tài)
AQS使用int類型來保存同步狀態(tài)烈菌,并且該狀態(tài)變量(status)被聲明為是volatile變量,并暴露出getState花履、setState以及compareAndSet操作來讀取和更新這個(gè)同步狀態(tài)芽世,由于compareAndSet是CAS操作,且變量本身是volatile變量诡壁,所以通過暴露這三個(gè)方法就達(dá)到了同步狀態(tài)的原子性管理济瓢,確保了同步狀態(tài)的原子性、可見性和有序性妹卿。
2.2 阻塞 LockSupport
AQS也是調(diào)用的LockSupport.park阻塞當(dāng)前線程直到有個(gè)LockSupport.unpark方法被調(diào)用旺矾。
LockSupport
在Java多線程中,當(dāng)需要阻塞或者喚醒一個(gè)線程時(shí)夺克,都會(huì)使用LockSupport工具類來完成相應(yīng)的工作箕宙。LockSupport定義了一組公共靜態(tài)方法,這些方法提供了最基本的線程阻塞和喚醒功能铺纽,而LockSupport也因此成為了構(gòu)建同步組件的基礎(chǔ)工具柬帕。
LockSupport定義了一組以park開頭的方法用來阻塞當(dāng)前線程,以及unpark(Thread)方法來喚醒一個(gè)被阻塞的線程,這些方法描述如下:
1.void park() 阻塞當(dāng)前線程陷寝,如果調(diào)用unpark(Thread)方法或被中斷锅很,才能從park()返回
2.void parkNanos(long nanos) 阻塞當(dāng)前線程,超時(shí)返回盼铁,阻塞時(shí)間最長不超過nanos納秒
3.void parkUntil(long deadline) 阻塞當(dāng)前線程粗蔚,直到deadline時(shí)間點(diǎn)
4.void unpark(Thread) 喚醒處于阻塞狀態(tài)的線程
5.park(Object blocker) 阻塞當(dāng)前線程,參數(shù)blocker是用來標(biāo)識當(dāng)前線程在等待的對象饶火,該對象主要用于問題排查和系統(tǒng)監(jiān)控
6.parkNanos(Object blocker, long nanos) 阻塞當(dāng)前線程鹏控,超時(shí)返回,阻塞時(shí)間最長不超過nanos納秒肤寝,參數(shù)blocker是用來標(biāo)識當(dāng)前線程在等待的對象当辐,該對象主要用于問題排查和系統(tǒng)監(jiān)控
7.parkUntil(Object blocker, long deadline) 阻塞當(dāng)前線程,直到deadline時(shí)間點(diǎn)鲤看,參數(shù)blocker是用來標(biāo)識當(dāng)前線程在等待的對象缘揪,該對象主要用于問題排查和系統(tǒng)監(jiān)控
2.3 同步隊(duì)列
AQS整個(gè)框架的核心就是如何管理線程阻塞隊(duì)列,他通過內(nèi)置的FIFO雙向隊(duì)列來完成線程的排隊(duì)工作义桂。該隊(duì)列內(nèi)部通過結(jié)點(diǎn)head和tail記錄隊(duì)首和隊(duì)尾元素,而該隊(duì)列的元素類型均為一個(gè)內(nèi)部類Node找筝。
Node
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
final boolean isShared() {
return nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
先看下waitStatus的幾種狀態(tài)
1.CANCELLED=1 表示線程因?yàn)橹袛嗷蛘叩却瑫r(shí),需要從等待隊(duì)列中取消等待慷吊,被取消的節(jié)點(diǎn)會(huì)被踢出隊(duì)列袖裕;
2.SIGNAL=-1 表示后續(xù)節(jié)點(diǎn)的線程處于等待狀態(tài),當(dāng)前節(jié)點(diǎn)的線程如果釋放了同步狀態(tài)或者被取消溉瓶,會(huì)通知后繼節(jié)點(diǎn)急鳄,后繼節(jié)點(diǎn)會(huì)獲取鎖并執(zhí)行(當(dāng)一個(gè)節(jié)點(diǎn)的狀態(tài)為SIGNAL時(shí)就意味著在等待獲取同步狀態(tài),前節(jié)點(diǎn)是頭節(jié)點(diǎn)也就是獲取同步狀態(tài)的節(jié)點(diǎn))堰酿;
3.CONDITION=-2 表示節(jié)點(diǎn)在條件等待隊(duì)列中疾宏,當(dāng)其他線程調(diào)用了Condition的signal()方法之后,該節(jié)點(diǎn)就會(huì)從條件等待隊(duì)列移動(dòng)到同步隊(duì)列中触创;
4.PROPAGTE=-3 表示下一次共享式同步狀態(tài)獲取將會(huì)傳遞給后繼節(jié)點(diǎn)獲取這個(gè)共享同步狀態(tài)(讀寫鎖中存在的狀態(tài)坎藐,代表后續(xù)還有資源,可以多個(gè)線程同時(shí)擁有同步狀態(tài))哼绑;
入隊(duì)操作
如圖所示顺饮,入隊(duì)的操作主要分為兩步
1.為了確保線程安全,同步器提供了一個(gè)CAS方法凌那,它需要傳遞當(dāng)前線程“認(rèn)為”的尾節(jié)點(diǎn)和當(dāng)前節(jié)點(diǎn),將"認(rèn)為"的尾結(jié)點(diǎn)和實(shí)際的尾結(jié)點(diǎn)比對成功才會(huì)將尾結(jié)點(diǎn)和新入隊(duì)的節(jié)點(diǎn)進(jìn)行關(guān)聯(lián)
2.上述CAS操作成功后吟逝,當(dāng)前節(jié)點(diǎn)才正式與之前的尾結(jié)點(diǎn)建立關(guān)聯(lián)
出隊(duì)操作
遵循FIFO規(guī)則帽蝶,能成功獲取到AQS同步狀態(tài)的必定是首節(jié)點(diǎn),故和入隊(duì)操作不同,出隊(duì)操作沒必要CAS處理励稳,只需要將首節(jié)點(diǎn)設(shè)置為原首節(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)同時(shí)斷開原節(jié)點(diǎn)佃乘、后續(xù)節(jié)點(diǎn)的引用即可。
2.4 條件隊(duì)列
前文介紹node節(jié)點(diǎn)的waitStatus時(shí)提到驹尼,當(dāng)值為-2時(shí)趣避,表示節(jié)點(diǎn)在條件隊(duì)列中,也就是說不同于前文的同步隊(duì)列新翎,在AQS中還存在一個(gè)條件隊(duì)列程帕,也就是一個(gè)ConditionObject的內(nèi)部類,如下圖
ConditionObject實(shí)現(xiàn)了Condition接口地啰,Condition接口提供了類似Object管程式的方法愁拭,如await、signal和signalAll操作亏吝,這里對Condition接口的方法做下簡單的介紹
//當(dāng)前線程進(jìn)入等待狀態(tài)岭埠,直到被通知(signal)或被中斷
void await() throws InterruptedException;
//當(dāng)前線程進(jìn)入等待狀態(tài),直到被通知(signal),對于中斷不做響應(yīng)
void awaitUninterruptibly();
//當(dāng)前線程進(jìn)入等待狀態(tài)蔚鸥,等待指定時(shí)長(單位為毫秒)惜论,直到被通知、中斷止喷,或者超時(shí)
long awaitNanos(long nanosTimeout) throws InterruptedException;
//當(dāng)前線程進(jìn)入等待狀態(tài)馆类,等待指定時(shí)長,直到被通知启盛、中斷蹦掐,或者超時(shí)
boolean await(long time, TimeUnit unit) throws InterruptedException;
//當(dāng)前線程進(jìn)入等待狀態(tài),直到被通知僵闯、中斷卧抗,或者到達(dá)指定時(shí)間。如果沒有到指定時(shí)間就被通知鳖粟,方法返回true社裆,否則false
boolean awaitUntil(Date deadline) throws InterruptedException;
// 喚醒一個(gè)等待線程,該線程從等待方法返回前必須獲得與Condition相關(guān)聯(lián)的鎖
void signal();
// 喚醒所有等待線程向图,該線程從等待方法返回前必須獲得與Condition相關(guān)聯(lián)的鎖
void signalAll();
ConditionObject中存在一個(gè)firstWaiter和一個(gè)lastWaiter泳秀,也就是說他有自己單獨(dú)的隊(duì)列,signal操作是通過將節(jié)點(diǎn)從條件隊(duì)列轉(zhuǎn)移到同步隊(duì)列中來實(shí)現(xiàn)的榄攀,而await操作就是當(dāng)前線程節(jié)點(diǎn)從同步隊(duì)列進(jìn)入條件隊(duì)列進(jìn)行等待嗜傅。
AQS只有一個(gè)同步隊(duì)列,但是可以有多個(gè)條件隊(duì)列檩赢。
2.5 資源共享方式
在前文給出Node節(jié)點(diǎn)的源碼時(shí)吕嘀,可以發(fā)現(xiàn)在Node節(jié)點(diǎn)中還存在SHARED 和 EXCLUSIVE 兩個(gè)靜態(tài)常量。是的,AQS定義兩種資源共享方式:Exclusive(獨(dú)占偶房,只有一個(gè)線程能執(zhí)行趁曼,如ReentrantLock)和Share(共享,多個(gè)線程可同時(shí)執(zhí)行棕洋,如Semaphore/CountDownLatch)挡闰。
不同的自定義同步器爭用共享資源的方式也不同。自定義同步器在實(shí)現(xiàn)時(shí)只需要實(shí)現(xiàn)共享資源state的獲取與釋放方式即可掰盘。
自定義同步器實(shí)現(xiàn)時(shí)主要實(shí)現(xiàn)以下幾種方法:
1.isHeldExclusively():該線程是否正在獨(dú)占資源摄悯。只有用到condition才需要去實(shí)現(xiàn)它。
2.tryAcquire(int):獨(dú)占方式庆杜。嘗試獲取資源射众,成功則返回true,失敗則返回false晃财。
3.tryRelease(int):獨(dú)占方式叨橱。嘗試釋放資源,成功則返回true断盛,失敗則返回false罗洗。
4.tryAcquireShared(int):共享方式。嘗試獲取資源钢猛。負(fù)數(shù)表示失敾锊恕;0表示成功命迈,但沒有剩余可用資源贩绕;正數(shù)表示成功,且有剩余資源壶愤。
5.tryReleaseShared(int):共享方式淑倾。嘗試釋放資源,如果釋放后允許喚醒后續(xù)等待結(jié)點(diǎn)返回true征椒,否則返回false娇哆。
三、AQS源碼分析
以上分析了大概的原理勃救,接下來看看主要的幾個(gè)方法的源碼
3.1 acquire(int)
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
acquire是一種以獨(dú)占方式獲取資源,如果獲取到了碍讨,就會(huì)直接返回,否則會(huì)被添加到隊(duì)列中進(jìn)行等待蒙秒,直到獲取到資源為止勃黍,并且這個(gè)操作是忽略中斷的。通過上面的源碼可以看到晕讲,該方法調(diào)用了以下幾個(gè)方法
1.tryAcquire() 用于獲取資源覆获,如果獲取到了就直接返回
2.addWaiter() 添加到同步等待隊(duì)列的隊(duì)尾榜田,這里傳入的Node.EXCLUSIVE表示是獨(dú)占模式
3.acquireQueued() 使線程在同步隊(duì)列中進(jìn)行等待,獲取到資源后才會(huì)返回锻梳,整個(gè)等待過程中被中斷過,則返回true净捅,否則返回false疑枯。
4.selfInterrupt() 獲取到資源后,如果acquireQueued() 被中斷過蛔六,則if條件判斷為空荆永,會(huì)進(jìn)到這里進(jìn)行自我中斷
詳細(xì)來看看
3.1.1tryAcquire()
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
默認(rèn)的實(shí)現(xiàn)只是單純拋了個(gè)異常,具體的需要子類進(jìn)行自定義擴(kuò)展国章,該方法表示嘗試以獨(dú)占的方式直接獲取資源具钥,如果獲取成功,則直接返回true液兽,否則直接返回false
3.1.2 addWaiter()
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
首先獲取到隊(duì)列的尾結(jié)點(diǎn)骂删。
如果尾結(jié)點(diǎn)不為空,則通過compareAndSetTail(pred, node)方法以CAS操作將尾結(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)四啰,如果設(shè)置成功宁玫,則將當(dāng)前節(jié)點(diǎn)和前節(jié)點(diǎn)進(jìn)行關(guān)聯(lián)。
如果尾結(jié)點(diǎn)為空柑晒,則表示隊(duì)列還未初始化欧瘪,此時(shí)會(huì)調(diào)用enq()方法,這里也貼出enq方法的源碼
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
可以看到這里采用CAS自旋的方式初始化隊(duì)列匙赞,第一次循環(huán)時(shí)會(huì)將頭結(jié)點(diǎn)和尾結(jié)點(diǎn)均設(shè)置為一個(gè)空的Node(new Node())佛掖,第二次循環(huán)時(shí)進(jìn)入else分支,將當(dāng)前節(jié)點(diǎn)設(shè)置成為尾結(jié)點(diǎn)涌庭。
3.1.3acquireQueued()
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
//如果是頭結(jié)點(diǎn)且獲取資源成功
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
可以看到該方法分為兩部分芥被,第一部分為如果是頭結(jié)點(diǎn)則直接嘗試獲取資源,如果獲取成功則返回脾猛,第二部分為如果不是頭結(jié)點(diǎn)或者資源沒有獲取成功撕彤,則會(huì)判斷當(dāng)前節(jié)點(diǎn)是否應(yīng)該park以及檢查是否中斷。
該方法實(shí)際是一個(gè)自旋猛拴,只有當(dāng)拿到鎖之后才會(huì)返回羹铅。接下來進(jìn)一步的看看shouldParkAfterFailedAcquire和parkAndCheckInterrupt
shouldParkAfterFailedAcquire()
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
如果前節(jié)點(diǎn)是SIGNAL狀態(tài)牌里,表示當(dāng)前節(jié)點(diǎn)應(yīng)該處于等待狀態(tài)借卧,需要中斷,如果前節(jié)點(diǎn)狀態(tài)>0赊琳,表示前節(jié)點(diǎn)已被取消跛溉,則會(huì)剔除前面所有取消的節(jié)點(diǎn)(循環(huán)將第一個(gè)不是取消狀態(tài)的節(jié)點(diǎn)設(shè)置為當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn))焊切,其他狀態(tài)則將當(dāng)前節(jié)點(diǎn)設(shè)置為SIGNAL狀態(tài)扮授,表示當(dāng)前節(jié)點(diǎn)可以獲取同步狀態(tài)。
parkAndCheckInterrupt()
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
讓線程waiting专肪,調(diào)用park方法后刹勃,除非被中斷或者unpark才會(huì)被喚醒,然后通過Thread.interrupted()清除線程的中斷標(biāo)記嚎尤。
所以回顧一下就會(huì)發(fā)現(xiàn)荔仁,整個(gè)acquire()方法的過程為
1.tryAcquire() 用于獲取資源,如果獲取到了就直接返回
2.addWaiter() 以Node.EXCLUSIVE獨(dú)占模式添加到同步等待隊(duì)列的隊(duì)尾芽死,如果隊(duì)列為空則首先將隊(duì)頭和隊(duì)尾設(shè)置為一個(gè)new Node()然后將當(dāng)前節(jié)點(diǎn)添加進(jìn)來
3.acquireQueued() 使線程在同步隊(duì)列中進(jìn)行等待乏梁,獲取到資源后才會(huì)返回,如果當(dāng)前節(jié)點(diǎn)是隊(duì)頭关贵,則直接嘗試獲取資源遇骑,成功后返回,否則會(huì)檢查當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)的狀態(tài)揖曾,以判斷當(dāng)前節(jié)點(diǎn)的線程是否應(yīng)該被park落萎,如果整個(gè)等待過程中被中斷過,則返回true翩肌,否則返回false模暗。
4.selfInterrupt() 獲取到資源后,如果acquireQueued() 被中斷過念祭,則if條件判斷為空兑宇,會(huì)進(jìn)到這里進(jìn)行自我中斷
3.2 release()
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
release方法比較簡單,直接通過tryRelease來釋放資源粱坤,如果成功了則喚醒頭結(jié)點(diǎn)的后續(xù)節(jié)點(diǎn)隶糕,unparkSuccessor中unpark了后繼節(jié)點(diǎn)的線程,如下面貼上的代碼所示
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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;
}
if (s != null)
LockSupport.unpark(s.thread);
}
3.3 acquireShared()
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
可以看到站玄,acquireShared也會(huì)直接嘗試獲取資源枚驻,如果獲取失敗則進(jìn)入在doAcquireShared中首先以Node.SHARED的狀態(tài)添加到隊(duì)尾,同時(shí)當(dāng)前節(jié)點(diǎn)的前節(jié)點(diǎn)(node.predecessor()獲取)是head時(shí)株旷,會(huì)再次獲取資源再登,和獨(dú)占模式不同的是,如果獲取成功晾剖,且剩余資源>0锉矢,會(huì)通過調(diào)用setHeadAndPropagate()來釋放剩余資源并喚醒后續(xù)節(jié)點(diǎn)線程, doReleaseShared()后文會(huì)說明齿尽。
3.4 releaseShared()
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
簡單來說就是釋放資源喚醒后續(xù)節(jié)點(diǎn)沽损,但是和獨(dú)占模式不同的是,獨(dú)占模式在當(dāng)前資源釋放成功后循头,如果頭結(jié)點(diǎn)的狀態(tài)不等于0就會(huì)喚醒頭結(jié)點(diǎn)的線程绵估,而共享模式下炎疆,就算頭結(jié)點(diǎn)的狀態(tài)已經(jīng)是0了,還會(huì)用CAS操作將同步狀態(tài)傳遞下去国裳,除非傳遞失敗形入。