AbstractQueuedSynchronizer 原理分析 - Condition 实现原理
1. 簡(jiǎn)介
Condition是一個(gè)接口,AbstractQueuedSynchronizer 中的ConditionObject內(nèi)部類實(shí)現(xiàn)了這個(gè)接口。Condition聲明了一組等待/通知的方法,這些方法的功能與Object中的wait/notify/notifyAll等方法相似。這兩者相同的地方在于,它們所提供的等待/通知方法均是為了協(xié)同線程的運(yùn)行秩序。只不過(guò),Object 中的方法需要配合 synchronized 關(guān)鍵字使用,而 Condition 中的方法則要配合鎖對(duì)象使用,并通過(guò)newCondition方法獲取實(shí)現(xiàn)類對(duì)象。除此之外,Condition 接口中聲明的方法功能上更為豐富一些。比如,Condition 聲明了具有不響應(yīng)中斷和超時(shí)功能的等待接口,這些都是 Object wait 方法所不具備的。
本篇文章是上一篇文章AbstractQueuedSynchronizer 原理分析 - 獨(dú)占/共享模式的續(xù)篇,在學(xué)習(xí) Condition 的原理前,建議大家先去了解 AbstractQueuedSynchronizer 同步隊(duì)列相關(guān)原理。本篇文章會(huì)涉及到同步隊(duì)列相關(guān)知識(shí),這些知識(shí)在上一篇文章分析過(guò)。
關(guān)于Condition的簡(jiǎn)介這里先說(shuō)到這,接下來(lái)分析一下Condition實(shí)現(xiàn)類ConditionObject的原理。
?2. 實(shí)現(xiàn)原理
ConditionObject是通過(guò)基于單鏈表的條件隊(duì)列來(lái)管理等待線程的。線程在調(diào)用await方法進(jìn)行等待時(shí),會(huì)釋放同步狀態(tài)。同時(shí)線程將會(huì)被封裝到一個(gè)等待節(jié)點(diǎn)中,并將節(jié)點(diǎn)置入條件隊(duì)列尾部進(jìn)行等待。當(dāng)有線程在獲取獨(dú)占鎖的情況下調(diào)用signal或singalAll方法時(shí),隊(duì)列中的等待線程將會(huì)被喚醒,重新競(jìng)爭(zhēng)鎖。另外,需要說(shuō)明的是,一個(gè)鎖對(duì)象可同時(shí)創(chuàng)建多個(gè) ConditionObject 對(duì)象,這意味著多個(gè)競(jìng)爭(zhēng)同一獨(dú)占鎖的線程可在不同的條件隊(duì)列中進(jìn)行等待。在喚醒時(shí),可喚醒指定條件隊(duì)列中的線程。其大致示意圖如下:
以上就是 ConditionObject 所實(shí)現(xiàn)的等待/通知機(jī)制的大致原理,并不是很難理解。當(dāng)然,在具體的實(shí)現(xiàn)中,則考慮的更為細(xì)致一些。相關(guān)細(xì)節(jié)將會(huì)在接下來(lái)一章中進(jìn)行說(shuō)明,繼續(xù)往下看吧。
?3. 源碼解析
?3.1 等待
ConditionObject 中實(shí)現(xiàn)了幾種不同的等待方法,每種方法均有它自己的特點(diǎn)。比如await()會(huì)響應(yīng)中斷,而awaitUninterruptibly()則不響應(yīng)中斷。await(long, TimeUnit)則會(huì)在響應(yīng)中斷的基礎(chǔ)上,新增了超時(shí)功能。除此之外,還有一些等待方法,這里就不一一列舉了。
在本節(jié)中,我將主要分析await()的方法實(shí)現(xiàn)。其他的等待方法大同小異,就不一一分析了,有興趣的朋友可以自己看一下。好了,接下來(lái)進(jìn)入源碼分析階段。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 | /*** await 是一個(gè)響應(yīng)中斷的等待方法,主要邏輯流程如下:* 1. 如果線程中斷了,拋出 InterruptedException 異常* 2. 將線程封裝到節(jié)點(diǎn)對(duì)象里,并將節(jié)點(diǎn)添加到條件隊(duì)列尾部* 3. 保存并完全釋放同步狀態(tài),保存下來(lái)的同步狀態(tài)在重新競(jìng)爭(zhēng)鎖時(shí)會(huì)用到* 4. 線程進(jìn)入等待狀態(tài),直到被通知或中斷才會(huì)恢復(fù)運(yùn)行* 5. 使用第3步保存的同步狀態(tài)去競(jìng)爭(zhēng)獨(dú)占鎖*/ public final void await() throws InterruptedException {// 線程中斷,則拋出中斷異常,對(duì)應(yīng)步驟1if (Thread.interrupted())throw new InterruptedException();// 添加等待節(jié)點(diǎn)到條件隊(duì)列尾部,對(duì)應(yīng)步驟2Node node = addConditionWaiter();// 保存并完全釋放同步狀態(tài),對(duì)應(yīng)步驟3。此方法的意義會(huì)在后面詳細(xì)說(shuō)明。int savedState = fullyRelease(node);int interruptMode = 0;/** 判斷節(jié)點(diǎn)是否在同步隊(duì)列上,如果不在則阻塞線程。* 循環(huán)結(jié)束的條件:* 1. 其他線程調(diào)用 singal/singalAll,node 將會(huì)被轉(zhuǎn)移到同步隊(duì)列上。node 對(duì)應(yīng)線程將* 會(huì)在獲取同步狀態(tài)的過(guò)程中被喚醒,并走出 while 循環(huán)。* 2. 線程在阻塞過(guò)程中產(chǎn)生中斷*/ while (!isOnSyncQueue(node)) {// 調(diào)用 LockSupport.park 阻塞當(dāng)前線程,對(duì)應(yīng)步驟4LockSupport.park(this);/** 檢測(cè)中斷模式,這里有兩種中斷模式,如下:* THROW_IE:* 中斷在 node 轉(zhuǎn)移到同步隊(duì)列“前”發(fā)生,需要當(dāng)前線程自行將 node 轉(zhuǎn)移到同步隊(duì)* 列中,并在隨后拋出 InterruptedException 異常。* * REINTERRUPT:* 中斷在 node 轉(zhuǎn)移到同步隊(duì)列“期間”或“之后”發(fā)生,此時(shí)表明有線程正在調(diào)用 * singal/singalAll 轉(zhuǎn)移節(jié)點(diǎn)。在該種中斷模式下,再次設(shè)置線程的中斷狀態(tài)。* 向后傳遞中斷標(biāo)志,由后續(xù)代碼去處理中斷。*/if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}/** 被轉(zhuǎn)移到同步隊(duì)列的節(jié)點(diǎn) node 將在 acquireQueued 方法中重新獲取同步狀態(tài),注意這里* 的這里的 savedState 是上面調(diào)用 fullyRelease 所返回的值,與此對(duì)應(yīng),可以把這里的 * acquireQueued 作用理解為 fullyAcquire(并不存在這個(gè)方法)。* * 如果上面的 while 循環(huán)沒(méi)有產(chǎn)生中斷,則 interruptMode = 0。但 acquireQueued 方法* 可能會(huì)產(chǎn)生中斷,產(chǎn)生中斷時(shí)返回 true。這里仍將 interruptMode 設(shè)為 REINTERRUPT,* 目的是繼續(xù)向后傳遞中斷,acquireQueued 不會(huì)處理中斷。*/if (acquireQueued(node, savedState) && interruptMode != THROW_IE)interruptMode = REINTERRUPT;/** 正常通過(guò) singal/singalAll 轉(zhuǎn)移節(jié)點(diǎn)到同步隊(duì)列時(shí),nextWaiter 引用會(huì)被置空。* 若發(fā)生線程產(chǎn)生中斷(THROW_IE)或 fullyRelease 方法出現(xiàn)錯(cuò)誤等異常情況,* 該引用則不會(huì)被置空*/ if (node.nextWaiter != null) // clean up if cancelled// 清理等待狀態(tài)非 CONDITION 的節(jié)點(diǎn)unlinkCancelledWaiters();if (interruptMode != 0)/** 根據(jù) interruptMode 覺(jué)得中斷的處理方式:* THROW_IE:拋出 InterruptedException 異常* REINTERRUPT:重新設(shè)置線程中斷標(biāo)志*/ reportInterruptAfterWait(interruptMode); }/** 將當(dāng)先線程封裝成節(jié)點(diǎn),并將節(jié)點(diǎn)添加到條件隊(duì)列尾部 */ private Node addConditionWaiter() {Node t = lastWaiter;/** 清理等待狀態(tài)為 CANCELLED 的節(jié)點(diǎn)。fullyRelease 內(nèi)部調(diào)用 release 發(fā)生異常或釋放同步狀* 態(tài)失敗時(shí),節(jié)點(diǎn)的等待狀態(tài)會(huì)被設(shè)置為 CANCELLED。所以這里要清理一下已取消的節(jié)點(diǎn)*/if (t != null && t.waitStatus != Node.CONDITION) {unlinkCancelledWaiters();t = lastWaiter;}// 創(chuàng)建節(jié)點(diǎn),并將節(jié)點(diǎn)置于隊(duì)列尾部Node node = new Node(Thread.currentThread(), Node.CONDITION);if (t == null)firstWaiter = node;elset.nextWaiter = node;lastWaiter = node;return node; }/** 清理等待狀態(tài)為 CANCELLED 的節(jié)點(diǎn) */ private void unlinkCancelledWaiters() {Node t = firstWaiter;// 指向上一個(gè)等待狀態(tài)為非 CANCELLED 的節(jié)點(diǎn)Node trail = null;while (t != null) {Node next = t.nextWaiter;if (t.waitStatus != Node.CONDITION) {t.nextWaiter = null;/** trail 為 null,表明 next 之前的節(jié)點(diǎn)等待狀態(tài)均為 CANCELLED,此時(shí)更新 * firstWaiter 引用的指向。* trail 不為 null,表明 next 之前有節(jié)點(diǎn)的等待狀態(tài)為 CONDITION,這時(shí)將 * trail.nextWaiter 指向 next 節(jié)點(diǎn)。*/if (trail == null)firstWaiter = next;elsetrail.nextWaiter = next;// next 為 null,表明遍歷到條件隊(duì)列尾部了,此時(shí)將 lastWaiter 指向 trailif (next == null)lastWaiter = trail;}else// t.waitStatus = Node.CONDITION,則將 trail 指向 ttrail = t;t = next;} }/*** 這個(gè)方法用于完全釋放同步狀態(tài)。這里解釋一下完全釋放的原因:為了避免死鎖的產(chǎn)生,鎖的實(shí)現(xiàn)上* 一般應(yīng)該支持重入功能。對(duì)應(yīng)的場(chǎng)景就是一個(gè)線程在不釋放鎖的情況下可以多次調(diào)用同一把鎖的 * lock 方法進(jìn)行加鎖,且不會(huì)加鎖失敗,如失敗必然導(dǎo)致導(dǎo)致死鎖。鎖的實(shí)現(xiàn)類可通過(guò) AQS 中的整型成員* 變量 state 記錄加鎖次數(shù),每次加鎖,將 state++。每次 unlock 方法釋放鎖時(shí),則將 state--,* 直至 state = 0,線程完全釋放鎖。用這種方式即可實(shí)現(xiàn)了鎖的重入功能。*/ final int fullyRelease(Node node) {boolean failed = true;try {// 獲取同步狀態(tài)數(shù)值int savedState = getState();// 調(diào)用 release 釋放指定數(shù)量的同步狀態(tài)if (release(savedState)) {failed = false;return savedState;} else {throw new IllegalMonitorStateException();}} finally {// 如果 relase 出現(xiàn)異常或釋放同步狀態(tài)失敗,此處將 node 的等待狀態(tài)設(shè)為 CANCELLEDif (failed)node.waitStatus = Node.CANCELLED;} }/** 該方法用于判斷節(jié)點(diǎn) node 是否在同步隊(duì)列上 */ final boolean isOnSyncQueue(Node node) {/** 節(jié)點(diǎn)在同步隊(duì)列上時(shí),其狀態(tài)可能為 0、SIGNAL、PROPAGATE 和 CANCELLED 其中之一,* 但不會(huì)為 CONDITION,所以可已通過(guò)節(jié)點(diǎn)的等待狀態(tài)來(lái)判斷節(jié)點(diǎn)所處的隊(duì)列。* * node.prev 僅會(huì)在節(jié)點(diǎn)獲取同步狀態(tài)后,調(diào)用 setHead 方法將自己設(shè)為頭結(jié)點(diǎn)時(shí)被置為 * null,所以只要節(jié)點(diǎn)在同步隊(duì)列上,node.prev 一定不會(huì)為 null*/if (node.waitStatus == Node.CONDITION || node.prev == null)return false;/** 如果節(jié)點(diǎn)后繼被為 null,則表明節(jié)點(diǎn)在同步隊(duì)列上。因?yàn)闂l件隊(duì)列使用的是 nextWaiter 指* 向后繼節(jié)點(diǎn)的,條件隊(duì)列上節(jié)點(diǎn)的 next 指針均為 null。但僅以 node.next != null 條* 件斷定節(jié)點(diǎn)在同步隊(duì)列是不充分的。節(jié)點(diǎn)在入隊(duì)過(guò)程中,是先設(shè)置 node.prev,后設(shè)置 * node.next。如果設(shè)置完 node.prev 后,線程被切換了,此時(shí) node.next 仍然為 * null,但此時(shí) node 確實(shí)已經(jīng)在同步隊(duì)列上了,所以這里還需要進(jìn)行后續(xù)的判斷。*/if (node.next != null)return true;// 在同步隊(duì)列上,從后向前查找 node 節(jié)點(diǎn)return findNodeFromTail(node); }/** 由于同步隊(duì)列上的的節(jié)點(diǎn) prev 引用不會(huì)為空,所以這里從后向前查找 node 節(jié)點(diǎn) */ private boolean findNodeFromTail(Node node) {Node t = tail;for (;;) {if (t == node)return true;if (t == null)return false;t = t.prev;} }/** 檢測(cè)線程在等待期間是否發(fā)生了中斷 */ private int checkInterruptWhileWaiting(Node node) {return Thread.interrupted() ?(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :0; }/** * 判斷中斷發(fā)生的時(shí)機(jī),分為兩種:* 1. 中斷在節(jié)點(diǎn)被轉(zhuǎn)移到同步隊(duì)列前發(fā)生,此時(shí)返回 true* 2. 中斷在節(jié)點(diǎn)被轉(zhuǎn)移到同步隊(duì)列期間或之后發(fā)生,此時(shí)返回 false*/ final boolean transferAfterCancelledWait(Node node) {// 中斷在節(jié)點(diǎn)被轉(zhuǎn)移到同步隊(duì)列前發(fā)生,此時(shí)自行將節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列上,并返回 trueif (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {// 調(diào)用 enq 將節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中enq(node);return true;}/** 如果上面的條件分支失敗了,則表明已經(jīng)有線程在調(diào)用 signal/signalAll 方法了,這兩個(gè)* 方法會(huì)先將節(jié)點(diǎn)等待狀態(tài)由 CONDITION 設(shè)置為 0 后,再調(diào)用 enq 方法轉(zhuǎn)移節(jié)點(diǎn)。下面判斷節(jié)* 點(diǎn)是否已經(jīng)在同步隊(duì)列上的原因是,signal/signalAll 方法可能僅設(shè)置了等待狀態(tài),還沒(méi)* 來(lái)得及轉(zhuǎn)移節(jié)點(diǎn)就被切換走了。所以這里用自旋的方式判斷 signal/signalAll 是否已經(jīng)完* 成了轉(zhuǎn)移操作。這種情況表明了中斷發(fā)生在節(jié)點(diǎn)被轉(zhuǎn)移到同步隊(duì)列期間。*/while (!isOnSyncQueue(node))Thread.yield();}// 中斷在節(jié)點(diǎn)被轉(zhuǎn)移到同步隊(duì)列期間或之后發(fā)生,返回 falsereturn false; }/*** 根據(jù)中斷類型做出相應(yīng)的處理:* THROW_IE:拋出 InterruptedException 異常* REINTERRUPT:重新設(shè)置中斷標(biāo)志,向后傳遞中斷*/ private void reportInterruptAfterWait(int interruptMode)throws InterruptedException {if (interruptMode == THROW_IE)throw new InterruptedException();else if (interruptMode == REINTERRUPT)selfInterrupt(); }/** 中斷線程 */ static void selfInterrupt() {Thread.currentThread().interrupt(); } |
?3.2 通知
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | /** 將條件隊(duì)列中的頭結(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中 */ public final void signal() {// 檢查線程是否獲取了獨(dú)占鎖,未獲取獨(dú)占鎖調(diào)用 signal 方法是不允許的if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)// 將頭結(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中doSignal(first); }private void doSignal(Node first) {do {/** 將 firstWaiter 指向 first 節(jié)點(diǎn)的 nextWaiter 節(jié)點(diǎn),while 循環(huán)將會(huì)用到更新后的 * firstWaiter 作為判斷條件。*/ if ( (firstWaiter = first.nextWaiter) == null)lastWaiter = null;// 將頭結(jié)點(diǎn)從條件隊(duì)列中移除first.nextWaiter = null;/** 調(diào)用 transferForSignal 將節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中,如果失敗,且 firstWaiter* 不為 null,則再次進(jìn)行嘗試。transferForSignal 成功了,while 循環(huán)就結(jié)束了。*/} while (!transferForSignal(first) &&(first = firstWaiter) != null); }/** 這個(gè)方法用于將條件隊(duì)列中的節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中 */ final boolean transferForSignal(Node node) {/** 如果將節(jié)點(diǎn)的等待狀態(tài)由 CONDITION 設(shè)為 0 失敗,則表明節(jié)點(diǎn)被取消。* 因?yàn)?transferForSignal 中不存在線程競(jìng)爭(zhēng)的問(wèn)題,所以下面的 CAS * 失敗的唯一原因是節(jié)點(diǎn)的等待狀態(tài)為 CANCELLED。*/ if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))return false;// 調(diào)用 enq 方法將 node 轉(zhuǎn)移到同步隊(duì)列中,并返回 node 的前驅(qū)節(jié)點(diǎn) pNode p = enq(node);int ws = p.waitStatus;/** 如果前驅(qū)節(jié)點(diǎn)的等待狀態(tài) ws > 0,則表明前驅(qū)節(jié)點(diǎn)處于取消狀態(tài),此時(shí)應(yīng)喚醒 node 對(duì)應(yīng)的* 線程去獲取同步狀態(tài)。如果 ws <= 0,這里通過(guò) CAS 將節(jié)點(diǎn) p 的等待設(shè)為 SIGNAL。* 這樣,節(jié)點(diǎn) p 在釋放同步狀態(tài)后,才會(huì)喚醒后繼節(jié)點(diǎn) node。如果 CAS 設(shè)置失敗,則應(yīng)立即* 喚醒 node 節(jié)點(diǎn)對(duì)應(yīng)的線程。以免因 node 沒(méi)有被喚醒導(dǎo)致同步隊(duì)列掛掉。關(guān)于同步隊(duì)列的相關(guān)的* 知識(shí),請(qǐng)參考我的另一篇文章“AbstractQueuedSynchronizer 原理分析 - 獨(dú)占/共享模式”,* 鏈接為:http://t.cn/RuERpHl*/if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))LockSupport.unpark(node.thread);return true; } |
看完了 signal 方法的分析,下面再來(lái)看看 signalAll 的源碼分析,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public final void signalAll() {// 檢查線程是否獲取了獨(dú)占鎖if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)doSignalAll(first); }private void doSignalAll(Node first) {lastWaiter = firstWaiter = null;/** 將條件隊(duì)列中所有的節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中。與 doSignal 方法略有不同,主要區(qū)別在 * while 循環(huán)的循環(huán)條件上,下的循環(huán)只有在條件隊(duì)列中沒(méi)節(jié)點(diǎn)后才終止。*/ do {Node next = first.nextWaiter;// 將 first 節(jié)點(diǎn)從條件隊(duì)列中移除first.nextWaiter = null;// 轉(zhuǎn)移節(jié)點(diǎn)到同步隊(duì)列上transferForSignal(first);first = next; } while (first != null); } |
?4. 其他
在我閱讀 ConditionObject 源碼時(shí)發(fā)現(xiàn)了一個(gè)問(wèn)題 - await 方法竟然沒(méi)有做同步控制。而在 signal 和 signalAll 方法開(kāi)頭都會(huì)調(diào)用 isHeldExclusively 檢測(cè)線程是否已經(jīng)獲取了獨(dú)占鎖,未獲取獨(dú)占鎖調(diào)用這兩個(gè)方法會(huì)拋出異常。但在 await 方法中,卻沒(méi)有進(jìn)行相關(guān)的檢測(cè)。如果在正確的使用方式下調(diào)用 await 方法是不會(huì)出現(xiàn)問(wèn)題的,所謂正確的使用方式指的是在獲取鎖的情況下調(diào)用 await 方法。但如果沒(méi)獲取鎖就調(diào)用該方法,就會(huì)產(chǎn)生線程競(jìng)爭(zhēng)的情況,這將會(huì)對(duì)條件隊(duì)列的結(jié)構(gòu)造成破壞。這里再來(lái)看一下新增節(jié)點(diǎn)的方法源碼,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | private Node addConditionWaiter() {Node t = lastWaiter;if (t != null && t.waitStatus != Node.CONDITION) {unlinkCancelledWaiters();t = lastWaiter;}Node node = new Node(Thread.currentThread(), Node.CONDITION);// 存在競(jìng)爭(zhēng)時(shí)將會(huì)導(dǎo)致節(jié)點(diǎn)入隊(duì)出錯(cuò)if (t == null)firstWaiter = node;elset.nextWaiter = node;lastWaiter = node;return node; } |
假如現(xiàn)在有線程 t1 和 t2,對(duì)應(yīng)節(jié)點(diǎn) node1 和 node2。線程 t1 獲取了鎖,而 t2 未獲取鎖,此時(shí)條件隊(duì)列為空,即 firstWaiter = lastWaiter = null。演繹一下會(huì)導(dǎo)致條件隊(duì)列被破壞的場(chǎng)景,如下:
如上,如果線程是按照上面的順序執(zhí)行,這會(huì)導(dǎo)致隊(duì)列被破壞。firstWaiter 本應(yīng)該指向 node1,但結(jié)果卻指向了 node2,node1 被排擠出了隊(duì)列。這樣會(huì)導(dǎo)致什么問(wèn)題呢?這樣可能會(huì)導(dǎo)致線程 t1 一直阻塞下去。因?yàn)?signal/signalAll 是從條件隊(duì)列頭部轉(zhuǎn)移節(jié)點(diǎn)的,但 node1 不在隊(duì)列中,所以 node1 無(wú)法被轉(zhuǎn)移到同步隊(duì)列上。在不出現(xiàn)中斷的情況下,node1 對(duì)應(yīng)的線程 t1 會(huì)被永久阻塞住。
這里未對(duì) await 方法進(jìn)行同步控制,導(dǎo)致條件隊(duì)列出現(xiàn)問(wèn)題,應(yīng)該算 ConditionObject 實(shí)現(xiàn)上的一個(gè)缺陷了。關(guān)于這個(gè)缺陷,博客園博主?活在夢(mèng)裡?在他的文章?AbstractQueuedSynchronizer源碼解讀–續(xù)篇之Condition?中也提到了。并向 JDK 開(kāi)發(fā)者提了一個(gè) BUG,BUG 鏈接為?JDK-8187408,有興趣的同學(xué)可以去看看。
?5. 總結(jié)
到這里,Condition 的原理就分析完了。分析完 Condition 原理,關(guān)于 AbstractQueuedSynchronizer 的分析也就結(jié)束了。總體來(lái)說(shuō),通過(guò)分析 AQS 并寫(xiě)成博客,使我對(duì) AQS 的原理有了更深刻的認(rèn)識(shí)。AQS 是 JDK 中鎖和其他并發(fā)組件實(shí)現(xiàn)的基礎(chǔ),弄懂 AQS 原理對(duì)后續(xù)在分析各種鎖和其他同步組件大有裨益。
AQS 本身實(shí)現(xiàn)比較復(fù)雜,要處理各種各樣的情況。作為類庫(kù),AQS 要考慮和處理各種可能的情況,實(shí)現(xiàn)起來(lái)可謂非常復(fù)雜。不僅如此,AQS 還很好的封裝了同步隊(duì)列的管理,線程的阻塞與喚醒等基礎(chǔ)操作,大大降低了繼承類實(shí)現(xiàn)同步控制功能的復(fù)雜度。所以,在本文的最后,再次向 AQS 的作者,Java 大師Doug Lea致敬。
好了,本文到此結(jié)束,謝謝大家閱讀。
?參考
- AbstractQueuedSynchronizer源碼解讀–續(xù)篇之Condition
- 本文鏈接:?https://www.tianxiaobo.com/2018/05/04/AbstractQueuedSynchronizer-原理分析-Condition-實(shí)現(xiàn)原理/
from:?http://www.tianxiaobo.com/2018/05/04/AbstractQueuedSynchronizer-%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90-Condition-%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/?
總結(jié)
以上是生活随笔為你收集整理的AbstractQueuedSynchronizer 原理分析 - Condition 实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: AbstractQueuedSynchr
- 下一篇: Java 重入锁 ReentrantLo