大家都親切地稱呼這玩意為AQS优训,作者寫了注釋噠:
* Provides a framework for implementing blocking locks and related
* synchronizers (semaphores, events, etc) that rely on
* first-in-first-out (FIFO) wait queues.??This class is designed to
* be a useful basis for most kinds of synchronizers that rely on a
* single atomic {@code int} value to represent state. Subclasses
* must define the protected methods that change this state, and which
* define what that state means in terms of this object being acquired
* or released.??Given these, the other methods in this class carry
* out all queuing and blocking mechanics. Subclasses can maintain
* other state fields, but only the atomically updated {@code int}
* value manipulated using methods {@link #getState}, {@link
* #setState} and {@link #compareAndSetState} is tracked with respect
* to synchronization.
我來翻譯一下就是嗽仪,提供了一個(gè)用來實(shí)現(xiàn)阻塞鎖和相應(yīng)的synchronizer(信號量,事件等等)的框架绢掰,這個(gè)框架基于先進(jìn)先出的等待隊(duì)列这弧,這個(gè)類是被設(shè)計(jì)來作為大多數(shù)synchronizer的基礎(chǔ)检眯,它依靠一個(gè)具有原子性的int值去代表狀態(tài)(這個(gè)狀態(tài)我理解的就是當(dāng)前被控制的對象的狀態(tài)朱灿,比如你一個(gè)鎖的狀態(tài))续扔,它的子類必須定義一個(gè)protected的方法來改變這個(gè)狀態(tài)攻臀,這個(gè)狀態(tài)它定義了幾個(gè)焕数,比如什么值代表它是正在被占有纱昧,什么值代表它已經(jīng)被釋放等等,子類也可以自己定義一些自己的狀態(tài)堡赔。但是關(guān)于這個(gè)狀態(tài)的改變還是很重要的识脆,因?yàn)樗闹狄彩菓?yīng)該受到保護(hù)的,不能讓多個(gè)線程同時(shí)操作以免意外情況的發(fā)生善已。
看看它有哪些字段:
private transient volatile Node head;
private transient volatile Node tail;
private volatile int state;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
啊灼捂,就這些,其中Node這個(gè)數(shù)據(jù)結(jié)構(gòu)也是定義在AQS里面的换团,這個(gè)Node就是用來存儲(chǔ)等待的線程們的:
static final classNode {
???static finalNodeSHARED=newNode();
???static finalNodeEXCLUSIVE=null;
???static final intCANCELLED=1;
???static final intSIGNAL???= -1;
???static final intCONDITION= -2;
???static final intPROPAGATE= -3;
???volatile intwaitStatus;
???volatileNodeprev;
???volatileNodenext;
???volatileThreadthread;
???NodenextWaiter;
???final booleanisShared() {
returnnextWaiter==SHARED;
???}
???finalNodepredecessor()throwsNullPointerException {
Node p =prev;
??????? if(p ==null)
throw newNullPointerException();
??????? else
??????????? returnp;
???}
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, intwaitStatus) {// Used by Condition
???????this.waitStatus= waitStatus;
??????? this.thread= thread;
???}
}
很明顯的一個(gè)雙向鏈表結(jié)構(gòu)悉稠,這里我們可以看到有兩個(gè)特別定義的Node艘包,一個(gè)是SHARED的猛,一個(gè)是EXCLUSIVE,他們代表了線程對于資源控制的兩種不同的模式舌厨,SHARED代表共享模式揉燃,EXCLUSIVE代表獨(dú)占模式肴颊。
首先要說一下為什么需要分這些模式竟宋,這要涉及到程序?qū)τ谀承ο蟮牟僮髌鋵?shí)分為讀和寫蜗字,讀的話,多少個(gè)線程來讀都OK浇垦,對吧,大家讀的都是一樣的東西鹃操,沒必要只讓一個(gè)線程讀燃观,其他線程還得等胳徽,性能多差啊,這就是共享模式的作用非春,讓大家在執(zhí)行讀操作的時(shí)候一起~
獨(dú)占模式就更容易理解了闽撤,你的操作涉及到對對象的修改(并且這部分操作是原子操作涧卵,也就是它的執(zhí)行邏輯上是得要么一起做完要么沒開始做的),那么多線程是不是會(huì)破壞它的原子性腹尖?這也是我們使用鎖的原因柳恐,而且這個(gè)時(shí)候只能讓一個(gè)線程先做完這部分操作,再讓其他線程接著做热幔,這就是獨(dú)占模式啦乐设。
第一部分 獨(dú)占模式
好,那么我們來分析一波獨(dú)占模式是怎么獨(dú)占到資源的绎巨,入口函數(shù):
public final voidacquire(intarg) {
if(!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE),arg))
selfInterrupt();
}
這個(gè)tryAcquire():
protected booleantryAcquire(intarg) {
throw newUnsupportedOperationException();
}
可以看到是個(gè)只會(huì)拋異常的空的函數(shù)近尚,里面什么也沒有,因?yàn)槲覀傾QS只是個(gè)基礎(chǔ)類嘛场勤,不會(huì)把所有東西都實(shí)現(xiàn)戈锻,這個(gè)定制化的任務(wù)交給實(shí)現(xiàn)它的子類,簡而言之和媳,它的功能是去嘗試獲取資源啦格遭,然后是acquireQueued()方法:
final booleanacquireQueued(finalNode node, intarg) {
booleanfailed =true;
??? try{
booleaninterrupted =false;
??????? for(;;) {
finalNode p = node.predecessor();
??????????? if(p ==head&& tryAcquire(arg)) {
setHead(node);
???????????????p.next=null;// help GC
???????????????failed =false;
??????????????? returninterrupted;
???????????}
if(shouldParkAfterFailedAcquire(p,node) &&
??????????????? parkAndCheckInterrupt())
interrupted =true;
???????}
}finally{
if(failed)
cancelAcquire(node);
???}
}
這個(gè)方法呢,做的就是當(dāng)一個(gè)線程節(jié)點(diǎn)獲取資源成功(它必須是在線程等待隊(duì)列的第二個(gè)節(jié)點(diǎn)留瞳,因?yàn)橛信袛鄋ode.predecessor()==head)拒迅,至于為什么,可能是為了公平吧她倘,畢竟先進(jìn)隊(duì)列的等的更久, 成功后璧微,就把當(dāng)前線程節(jié)點(diǎn)node設(shè)置為頭節(jié)點(diǎn)然后將之前的p也就是頭結(jié)點(diǎn)的next設(shè)為null, 目的是讓它不被任何其他對象引用硬梁,下次GC時(shí)就會(huì)被回收掉前硫,并且返回interrupted,如果否荧止,這樣在acquire(intarg)方法中就不會(huì)去調(diào)用selfInterrupt()方法了屹电。如果獲取資源失敗了,就會(huì)首先判斷執(zhí)行shouldParkAfterFailedAcquire(p,node)這個(gè)方法(用來確定是否是要中斷線程):
private static booleanshouldParkAfterFailedAcquire(Node pred,Node node) {
intws = pred.waitStatus;
??? if(ws == Node.SIGNAL)
???????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;
}
這個(gè)方法是去判斷他的前驅(qū)節(jié)點(diǎn)的狀態(tài)罩息,如果是Signal的話嗤详,代表了它前面那個(gè)節(jié)點(diǎn)release的時(shí)候是會(huì)正常通知下個(gè)節(jié)點(diǎn)的所以它就能放心的阻塞自己。
如果說前驅(qū)節(jié)點(diǎn)狀態(tài)是Cancel那么就得忽略掉前面的節(jié)點(diǎn)瓷炮,把前驅(qū)節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)作為當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)(好拗口葱色,反正就是如果前驅(qū)節(jié)點(diǎn)是Cancel的,就跳過它再往前娘香,這個(gè)連等是自右向左賦值的苍狰,大家自己看代碼)
然后如果前驅(qū)節(jié)點(diǎn)的狀態(tài)不是以上兩種(那有可能是CONDITION或PROPAGATE)办龄,就講這個(gè)前驅(qū)節(jié)點(diǎn)的狀態(tài)設(shè)置為Node.SIGNAL。
如果這個(gè)方法返回的是true那么則會(huì)執(zhí)行parkAndCheckInterrupt()方法:
private final booleanparkAndCheckInterrupt() {
LockSupport.park(this);
??? returnThread.interrupted();
}
這個(gè)方法很簡單淋昭,就是將線程先停止一下俐填,具體呢?大家可以看我寫的另一篇關(guān)于LockSupport類的文章哈(http://www.reibang.com/p/6bffd19cb900不是我打廣告翔忽,反正你們也不給錢英融,而是你不看你都不知道LockSupport.park(this)這個(gè)方法干了些什么,如果我在這來講一遍歇式,那就又跑題啦)~
然后就返回這個(gè)線程是否被暫停驶悟,那么返回acquireQueued()方法,如果parkAndCheckInterrupt()返回的是true材失,那么在acquireQueued()方法中就會(huì)把interrupted這個(gè)變量賦值為true痕鳍,在acquireQueued()中是一個(gè)循環(huán),就是循環(huán)去tryAquire()獲取資源龙巨,方法返回值就是這個(gè)interrupted笼呆, 如果是true代表線程已經(jīng)被暫停了旨别,如果是false代表沒有被暫停。
然后注意到還有
finally{
if(failed)
cancelAcquire(node);
???}
這部分代碼胆屿,表示最終如果都沒能獲取這個(gè)資源的話,就執(zhí)行cancelAcquire()方法:
private voidcancelAcquire(Node node) {
// 如果節(jié)點(diǎn)是null的話就忽略
???if(node ==null)
return;
???node.thread=null;
???// 跳過已經(jīng)是cancel狀態(tài)的前置節(jié)點(diǎn)
???Node pred = node.prev;
??? while(pred.waitStatus>0)
node.prev= pred = pred.prev;
???Node predNext = pred.next;
???node.waitStatus= Node.CANCELLED;
???if(node ==tail&& compareAndSetTail(node,pred)) {
compareAndSetNext(pred,predNext, null);
???}else{
???????intws;
??????? 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{
unparkSuccessor(node);
???????}
node.next= node;// 幫助GC
???}
}
哎喲那么多行代碼看得腦殼疼,看看它都做了什么吵冒,一句話概括纯命,它完了,它會(huì)把它自己從這個(gè)Node鏈中移除痹栖,讓自己等著被GC回收亿汞,大家可以看到這里面調(diào)用了兩次compareAndSetNext()方法:
private static final booleancompareAndSetNext(Node node,
??????????????????????????????????????????????Node expect,
??????????????????????????????????????????????Node update) {
returnunsafe.compareAndSwapObject(node,nextOffset,expect,update);
}
其實(shí)就是調(diào)用Unsafe的方法通過CAS的方式來設(shè)置Node的next節(jié)點(diǎn),就是把當(dāng)前節(jié)點(diǎn)的前面節(jié)點(diǎn)指向當(dāng)前節(jié)點(diǎn)的后面節(jié)點(diǎn):
這里注意還有一個(gè)方法unparkSuccessor()揪阿,這個(gè)方法激活它后面的節(jié)點(diǎn):
private voidunparkSuccessor(Node node) {
???intws = node.waitStatus;
??? if(ws <0)
compareAndSetWaitStatus(node,ws,0);
???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);
}
可以看到疗我,如果當(dāng)前節(jié)點(diǎn)的狀態(tài)不是CANCELLED咆畏,那就設(shè)置當(dāng)前的狀態(tài)為0,然后下一步吴裤,操作它的下一個(gè)節(jié)點(diǎn)旧找,前提是這個(gè)節(jié)點(diǎn)不為null并且它的狀態(tài)不為CANCELLED, 如果為null或者CANCELLED,又要進(jìn)行下一波操作麦牺,將Node由尾向前遍歷钮蛛,找到最靠前的那個(gè)不為null并且不是當(dāng)前節(jié)點(diǎn)本身并且狀態(tài)不為CANCELLED的節(jié)點(diǎn),這個(gè)由后向前遍歷的操作是有點(diǎn)騷的啊剖膳,大家體會(huì)一下魏颓,因?yàn)樗膎ext都為null啦,所以沒法從next開始遍歷潮秘,于是往前遍歷了琼开。
言歸正傳,找到下一個(gè)滿足條件的后節(jié)點(diǎn)后枕荞,就會(huì)通過LockSupport.unpark(s.thread)方法將其喚醒~也就是輪到下個(gè)節(jié)點(diǎn)的線程進(jìn)行工作的時(shí)候啦柜候。
走到了這里,我們就可以回到acquire()方法啦, 我們是不是沿著acquire()的腳步走了一圈躏精?咦渣刷,漏了addWaiter(Node.EXCLUSIVE)這個(gè)方法:
privateNodeaddWaiter(Node mode) {
Node node =newNode(Thread.currentThread(),mode);
???Node pred =tail;
??? if(pred !=null) {
node.prev= pred;
??????? if(compareAndSetTail(pred,node)) {
pred.next= node;
??????????? returnnode;
???????}
??? }
enq(node);
??? returnnode;
}
這個(gè)方法返回的值是一個(gè)Node節(jié)點(diǎn),這個(gè)返回值是作為acquireQueued()參數(shù)傳入的矗烛,它接受的參數(shù)也是個(gè)Node辅柴,我們來看看這個(gè)方法的輸入和輸出有啥區(qū)別~
簡而言之當(dāng)這個(gè)AQS實(shí)例的tail這個(gè)節(jié)點(diǎn)不為空時(shí),它會(huì)將參數(shù)這個(gè)節(jié)點(diǎn)加入到AQS實(shí)例的Node鏈表中(加到末尾)瞭吃,你可以看到通過compareAndSetTail()方法(我不再細(xì)講這個(gè)方法了哈碌嘀,其實(shí)就是調(diào)用的Unsafe的方法,本質(zhì)是CAS來保證原子性)來重新設(shè)置了AQS實(shí)例的尾節(jié)點(diǎn)歪架,并返回這個(gè)節(jié)點(diǎn)股冗。
如果這個(gè)AQS實(shí)例的tail是空的話,就會(huì)執(zhí)行一個(gè)方法enq(node):
privateNodeenq(finalNode node) {
for(;;) {
Node t =tail;
??????? if(t ==null) {// Must initialize
???????????if(compareAndSetHead(newNode()))
tail=head;
???????}else{
node.prev= t;
??????????? if(compareAndSetTail(t,node)) {
t.next= node;
??????????????? returnt;
???????????}
??????? }
??? }
}
可以看到這個(gè)方法里有一個(gè)循環(huán)和蚪,在這里面又會(huì)去取到AQS實(shí)例的tail節(jié)點(diǎn)止状,再去判斷它是否為空,如果是空的攒霹,新建一個(gè)Node節(jié)點(diǎn)賦給頭結(jié)點(diǎn)怯疤,再把頭節(jié)點(diǎn)賦給尾節(jié)點(diǎn),其實(shí)就是初始化head和tail催束,然后tail就不為null了嘛集峦,就會(huì)走進(jìn)else這個(gè)分支,所以這里的for(;;)操作也很騷氣~它會(huì)一直等到tail不是null的時(shí)候return。else做的操作就很淺顯易懂了少梁,就是講要添加的node節(jié)點(diǎn)賦給tail作為整個(gè)鏈表的尾節(jié)點(diǎn)然后返回的就是當(dāng)前節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)(但這里調(diào)用enq()時(shí)并沒有接這個(gè)返回值洛口,只是調(diào)用而已)。
然后是acquire()方法中的最后一個(gè)方法selfInterrupt():
static voidselfInterrupt() {
Thread.currentThread().interrupt();
}
其實(shí)就是設(shè)置中斷的狀態(tài)為true凯沪,因?yàn)閍cquire里有個(gè)判斷acquireQueued(addWaiter(Node.EXCLUSIVE), arg)第焰,這里返回的是線程是否被中斷了,如果被中斷了才執(zhí)行selfInterrupt妨马,目的是將這個(gè)中斷狀態(tài)傳遞出去挺举,關(guān)于線程的中斷機(jī)制,我理解了老半天烘跺,感覺網(wǎng)上搜到文章都不太地道湘纵,所以我決定寫一篇文章專門講這個(gè),敬請期待~
那么獨(dú)占模式下AQS獲取資源的整個(gè)流程就走完了滤淳,是不是還暈暈的梧喷,是的!正常脖咐!那我們來總結(jié)一下流程(從http://www.javarticles.com/2012/10/abstractqueuedsynchronizer-aqs.html#prettyPhoto盜的一張圖~懶得自己畫了铺敌,諒解一下~):
一句話總結(jié)就是:線程會(huì)去嘗試獨(dú)占資源,如果成功就是將threadowner設(shè)為它自己然后接著做想做的事情屁擅,如果不成功就將這個(gè)線程放入一個(gè)先入先出的隊(duì)列等待偿凭,這是站在線程的角度看,它被放進(jìn)了一個(gè)隊(duì)列派歌,站在隊(duì)列的角度看弯囊,它還會(huì)讓隊(duì)列里滿足條件的線程去持續(xù)嘗試獨(dú)占資源,如果占成功了那就行了唄胶果,沒有的話就看要不要把線程給暫停了匾嘱,如果暫停了就等待被喚醒啦,如果沒暫停的就再嘗試占有資源早抠,一直就這樣~(這一句話貌似有點(diǎn)長=奄毡。=)
好~獲取資源部分終于差不多了,獲取了之后總要釋放唄贝或,下面就講獨(dú)占資源的釋放~
資源的釋放呢是調(diào)用的release方法:
public final booleanrelease(intarg) {
if(tryRelease(arg)) {
Node h =head;
??????? if(h !=null&& h.waitStatus!=0)
unparkSuccessor(h);
??????? return true;
???}
return false;
}
其中tryRelease(arg)這個(gè)就跟tryAcquire()一樣是由繼承它的子類實(shí)現(xiàn)的(如果想看例子請看ReentrantLock中的tryRelease方法),想想锐秦,這個(gè)方法也是當(dāng)前線程來執(zhí)行的對吧咪奖,那就意味著,當(dāng)前線程此刻是運(yùn)行的狀態(tài)酱床,這個(gè)就會(huì)通過unparkSuccessor(h)方法喚醒頭節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)羊赵,這個(gè)unparkSuccessor(h)方法前面已經(jīng)分析過了,反正就是喚醒下個(gè)節(jié)點(diǎn)。
然后釋放的工作就結(jié)束啦~
說完獨(dú)占模式后該說共享模式啦~期待得搓手手~
第二部分 共享模式
還是按照套路昧捷,將獲取資源的方法走一遍~
共享模式獲取資源調(diào)用的是acquireShared方法:
public final voidacquireShared(intarg) {
if(tryAcquireShared(arg) <0)
doAcquireShared(arg);
}
tryAcquireShared大家應(yīng)該能猜到是由子類實(shí)現(xiàn)了闲昭,也就是我們自己寫實(shí)現(xiàn),反正只要它小于0靡挥,我們實(shí)現(xiàn)的時(shí)候就要注意了序矩,小于0在這里就要代表沒有獲取成功,這樣就會(huì)執(zhí)行下面的操作 doAcquireShared(arg):
private voiddoAcquireShared(intarg) {
finalNode node = addWaiter(Node.SHARED);
??? booleanfailed =true;
??? try{
booleaninterrupted =false;
??????? for(;;) {
finalNode p = node.predecessor();
??????????? if(p ==head) {
intr = 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);
???}
}
首先同樣會(huì)執(zhí)行addWaiter(Node.SHARED)跋破,這個(gè)方法之前講了嘛簸淀,獨(dú)占模式也有,就是講當(dāng)前線程所在節(jié)點(diǎn)加入到等待隊(duì)列里面毒返,唯一不同的是這里設(shè)置的nextWaiter是Node.SHARED而已租幕。然后進(jìn)入循環(huán),如果當(dāng)前節(jié)點(diǎn)的前置節(jié)點(diǎn)是頭節(jié)點(diǎn)的話拧簸,會(huì)去接著嘗試tryAcquireShared(arg)試圖獲取資源劲绪,如果成功,則執(zhí)行setHeadAndPropagate方法:
private voidsetHeadAndPropagate(Node node, intpropagate) {
Node h =head;// 保存舊的head用于下面檢查
???setHead(node);
???if(propagate >0|| h ==null|| h.waitStatus<0||
(h =head) ==null|| h.waitStatus<0) {
Node s = node.next;
??????? if(s ==null|| s.isShared())
doReleaseShared();
???}
}
它這個(gè)方法里有一串判斷條件:propagate > 0 || h == null || h.waitStatus < 0 ||(h = head) == null || h.waitStatus < 0
翻譯:以下條件滿足其一:
? ? ? ? ? 1.?propagate也就是tryAcquireShared方法的返回值要大于0盆赤,也就是要獲取資源成功
? ? ? 2. 之前的頭節(jié)點(diǎn)是空
? ? ? 3. 之前的頭節(jié)點(diǎn)的等待狀態(tài)不是CANCELLED
? ? ? 4. 當(dāng)前頭節(jié)點(diǎn)為空
? ? ? 5. 當(dāng)前頭節(jié)點(diǎn)等待狀態(tài)不是CANCELLED
好贾富,滿足這幾個(gè)要求中的一個(gè)就能走到if里面去啦,看看它做了什么呢弟劲?
首先找到了當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)祷安,如果它是空的或者它的模式是共享模式,那么執(zhí)行doReleaseShared():
private voiddoReleaseShared() {
???for(;;) {
Node h =head;
??????? if(h !=null&& h !=tail) {
intws = h.waitStatus;
??????????? if(ws == Node.SIGNAL) {
if(!compareAndSetWaitStatus(h,Node.SIGNAL,0))
continue;????????????
???????????????unparkSuccessor(h);
???????????}
else if(ws ==0&&
!compareAndSetWaitStatus(h,0,Node.PROPAGATE))
continue;???????????????// CAS失敗后繼續(xù)循環(huán)
???????}
if(h ==head)// 如果頭節(jié)點(diǎn)不一樣了則循環(huán)繼續(xù)兔乞,否則跳出循環(huán)
???????????break;
???}
}
哇哦汇鞭,又是一個(gè)循環(huán),我們可以看到庸追,跳出循環(huán)的條件是h == head霍骄, 然鵝,h = head淡溯,那么我們就有兩個(gè)猜想读整,第一,這個(gè)head可能是在多線程的環(huán)境下發(fā)生了變化咱娶,不然在單線程的條件下米间,h == head是永遠(yuǎn)成立的, 第二膘侮,在上面這段:
?if(h !=null&& h !=tail) {
? ? ? ?int ws = h.waitStatus;
? ? ? ?if(ws == Node.SIGNAL) {
if(!compareAndSetWaitStatus(h,Node.SIGNAL,0))
? ? ? ? ? ? ? ?continue;????????????
???????????????unparkSuccessor(h);
???????????}
else if(ws ==0&&!compareAndSetWaitStatus(h,0,Node.PROPAGATE))
????????????continue;???????????????// CAS失敗后繼續(xù)循環(huán)
???????}
操作中可能有對head的修改屈糊,那么我們先來看看這段代碼有沒有修改head。
當(dāng)h不是空并且也不是最后一個(gè)節(jié)點(diǎn)的情況下琼了,我們拿到他的等待狀態(tài)逻锐,如果是SIGNAL(代表了它后面的節(jié)點(diǎn)需要被喚醒),然后我們就去將它的等待狀態(tài)設(shè)為0(初始狀態(tài),表示后面沒有節(jié)點(diǎn)等待被喚醒)昧诱,并unparkSuccessor(喚醒后面的節(jié)點(diǎn))晓淀,這個(gè)方面前面也講了,記不住翻上去看盏档,然后如果節(jié)點(diǎn)的等待狀態(tài)是0的話就回去設(shè)置它的狀態(tài)是Node.PROPAGATE凶掰,但是這個(gè)方法也沒有改變過head節(jié)點(diǎn)。
那么就意味著我們第一個(gè)猜想是正確的妆丘,head可能在多個(gè)線程的執(zhí)行過程中發(fā)生了變化锄俄,那么head沒變就代表了已經(jīng)沒有多個(gè)線程在這同時(shí)執(zhí)行這段代碼了(多個(gè)線程試圖獲取資源),也就可以退出循環(huán)了勺拣。
跳出之后奶赠,我們可以回到doAcquireShared方法了,我們已經(jīng)喚醒了后面的共享模式的節(jié)點(diǎn)药有,然后我們就把p(頭節(jié)點(diǎn)從等待隊(duì)列中刪除)
if(shouldParkAfterFailedAcquire(p,node) &&
??????????????? parkAndCheckInterrupt())
????interrupted =true;
這段代碼很熟悉了毅戈,獨(dú)占模式下也有,就是判斷當(dāng)獲取資源失敗時(shí)是否將線程中斷愤惰,并檢查線程是否被中斷苇经,賦值給interrupted。
如果是被中斷了的話宦言,就執(zhí)行selfInterrupt方法重置線程的中斷標(biāo)志為true扇单,然后就可以跳出循環(huán)了。反正不管跳不跳出循環(huán)奠旺,最終是要執(zhí)行下面這段代碼的:
finally{
if(failed)
cancelAcquire(node);
}
cancelAcquire()這個(gè)方法我們前面也講了蜘澜,就是將當(dāng)前線程給取消掉,一定條件下去喚醒后面的節(jié)點(diǎn)响疚。然后就完事~是不是很簡單鄙信,過程基本上和獨(dú)占的差不多,只是它會(huì)執(zhí)行setHeadAndPropagate這個(gè)方法忿晕,也就是如果它的下個(gè)節(jié)點(diǎn)也是SHARED模式的話装诡,它會(huì)將它的下個(gè)節(jié)點(diǎn)也喚醒。
好的大概講一下流程践盼,白話講哈鸦采,比較容易理解,就是線程首先會(huì)去獲取資源嘛咕幻,如果獲取到了就占有它赖淤,如果沒獲取到就加入等待隊(duì)列的末尾嘛,然后隊(duì)列中的線程都會(huì)去判斷一下谅河,它前面那個(gè)節(jié)點(diǎn)是不是頭節(jié)點(diǎn),因?yàn)轭^節(jié)點(diǎn)就是占有著資源的那個(gè)節(jié)點(diǎn)嘛,頭節(jié)點(diǎn)完了就可以輪到它了绷耍,所以它就不掛起自己吐限,接著請求,重復(fù)請求等著前面節(jié)點(diǎn)釋放資源嘛褂始,釋放了資源它就緊接著占有資源啦诸典,占有后,共享模式會(huì)把自己線程的節(jié)點(diǎn)設(shè)為頭節(jié)點(diǎn)了嘛崎苗,之前的頭已經(jīng)完成移出隊(duì)列了狐粱,然后現(xiàn)在就它自己是頭啦,它會(huì)去檢查自己后面那個(gè)節(jié)點(diǎn)是不是也是SHARED共享模式的胆数,如果是肌蜻,它就把它喚醒!當(dāng)然也有線程掛起的時(shí)候啦必尼,比如它前面的節(jié)點(diǎn)并不是頭節(jié)點(diǎn)的時(shí)候蒋搜,那么這個(gè)時(shí)候要把它前面那個(gè)節(jié)點(diǎn)的狀態(tài)設(shè)為Node.SIGNAL,這樣子當(dāng)他前面節(jié)點(diǎn)被喚醒判莉,執(zhí)行完成要將資源交出去的時(shí)候才會(huì)去喚醒它后面的節(jié)點(diǎn)豆挽,不然的話后面的節(jié)點(diǎn)就不能夠被喚醒哦,所以Node.SIGNAL很重要啦~
整個(gè)過程大概就是這樣子的券盅,共享模式的釋放也很簡單跟獨(dú)占模式類似所以也不講啦~
呼~好累帮哈,但是還是要多說兩句,這個(gè)AbstractQueuedSynchronizer所有方法我們都要考慮到是很多個(gè)線程同時(shí)在執(zhí)行的锰镀,雖然有的阻塞娘侍,有的在執(zhí)行,時(shí)刻這樣想著有助于我們理解這個(gè)代碼的一些設(shè)計(jì)~
最后就是互站,我也是自己試圖去把自己的理解寫出來的私蕾,有什么問題和建議都請留言,批評就算了胡桃,寫這個(gè)很累的踩叭,況且仙女不接受批評~