摘要:實(shí)現(xiàn)原理是通過(guò)基于單鏈表的條件隊(duì)列來(lái)管理等待線程的。中斷在轉(zhuǎn)移到同步隊(duì)列期間或之后發(fā)生,此時(shí)表明有線程正在調(diào)用轉(zhuǎn)移節(jié)點(diǎn)。在該種中斷模式下,再次設(shè)置線程的中斷狀態(tài)。
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)入源碼分析階段。
/** * 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)步驟1 if (Thread.interrupted()) throw new InterruptedException(); // 添加等待節(jié)點(diǎn)到條件隊(duì)列尾部,對(duì)應(yīng)步驟2 Node 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)步驟4 LockSupport.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; else t.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; else trail.nextWaiter = next; // next 為 null,表明遍歷到條件隊(duì)列尾部了,此時(shí)將 lastWaiter 指向 trail if (next == null) lastWaiter = trail; } else // t.waitStatus = Node.CONDITION,則將 trail 指向 t trail = 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)異?;蜥尫磐綘顟B(tài)失敗,此處將 node 的等待狀態(tài)設(shè)為 CANCELLED if (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ì)列上,并返回 true if (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ā)生,返回 false return 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 通知
/** 將條件隊(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) p Node 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 的源碼分析,如下:
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)的方法源碼,如下:
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; else t.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)景,如下:
時(shí)刻1:線程 t1 和 t2 同時(shí)執(zhí)行到 if (t == null),兩個(gè)線程都認(rèn)為 if 條件滿足
時(shí)刻2:線程 t1 初始化 firstWaiter,即將 firstWaiter 指向 node1
時(shí)刻3:線程 t2 再次修改 firstWaiter 的指向,此時(shí) firstWaiter 指向 node2
如上,如果線程是按照上面的順序執(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
本文在知識(shí)共享許可協(xié)議 4.0 下發(fā)布,轉(zhuǎn)載需在明顯位置處注明出處
作者:coolblog
本文同步發(fā)布在我的個(gè)人博客:http://www.coolblog.xyz
本作品采用知識(shí)共享署名-非商業(yè)性使用-禁止演繹 4.0 國(guó)際許可協(xié)議進(jìn)行許可。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/69299.html
摘要:實(shí)現(xiàn)原理是通過(guò)基于單鏈表的條件隊(duì)列來(lái)管理等待線程的。中斷在轉(zhuǎn)移到同步隊(duì)列期間或之后發(fā)生,此時(shí)表明有線程正在調(diào)用轉(zhuǎn)移節(jié)點(diǎn)。在該種中斷模式下,再次設(shè)置線程的中斷狀態(tài)。 1. 簡(jiǎn)介 Condition是一個(gè)接口,AbstractQueuedSynchronizer 中的ConditionObject內(nèi)部類實(shí)現(xiàn)了這個(gè)接口。Condition聲明了一組等待/通知的方法,這些方法的功能與Objec...
摘要:實(shí)現(xiàn)原理是通過(guò)基于單鏈表的條件隊(duì)列來(lái)管理等待線程的。中斷在轉(zhuǎn)移到同步隊(duì)列期間或之后發(fā)生,此時(shí)表明有線程正在調(diào)用轉(zhuǎn)移節(jié)點(diǎn)。在該種中斷模式下,再次設(shè)置線程的中斷狀態(tài)。 1. 簡(jiǎn)介 Condition是一個(gè)接口,AbstractQueuedSynchronizer 中的ConditionObject內(nèi)部類實(shí)現(xiàn)了這個(gè)接口。Condition聲明了一組等待/通知的方法,這些方法的功能與Objec...
摘要:同步器擁有三個(gè)成員變量隊(duì)列的頭結(jié)點(diǎn)隊(duì)列的尾節(jié)點(diǎn)和狀態(tài)。對(duì)于同步器維護(hù)的狀態(tài),多個(gè)線程對(duì)其的獲取將會(huì)產(chǎn)生一個(gè)鏈?zhǔn)降慕Y(jié)構(gòu)。使用將當(dāng)前線程,關(guān)于后續(xù)會(huì)詳細(xì)介紹。 簡(jiǎn)介提供了一個(gè)基于FIFO隊(duì)列,可以用于構(gòu)建鎖或者其他相關(guān)同步裝置的基礎(chǔ)框架。該同步器(以下簡(jiǎn)稱同步器)利用了一個(gè)int來(lái)表示狀態(tài),期望它能夠成為實(shí)現(xiàn)大部分同步需求的基礎(chǔ)。使用的方法是繼承,子類通過(guò)繼承同步器并需要實(shí)現(xiàn)它的方法來(lái)管理...
摘要:總結(jié)總的來(lái)說(shuō),操作順序是進(jìn)入隊(duì)列喚醒,成功獲得鎖將狀態(tài)變?yōu)椴⑵鋸霓D(zhuǎn)到使再次獲得鎖執(zhí)行余下代碼。當(dāng)然這是理由狀態(tài)下,為了討論及的原理,實(shí)際的操作時(shí)序也有可能變化。 AQS Condition 最近面試被問(wèn)到j(luò)ava concurrent包下有哪些熟悉的,用過(guò)的工具。因此來(lái)回顧一下,這些工具的底層實(shí)現(xiàn),AbstractQueuedSynchronizer。在網(wǎng)上看到了其他人的一些技術(shù)博客...
摘要:總結(jié)總的來(lái)說(shuō),操作順序是進(jìn)入隊(duì)列喚醒,成功獲得鎖將狀態(tài)變?yōu)椴⑵鋸霓D(zhuǎn)到使再次獲得鎖執(zhí)行余下代碼。當(dāng)然這是理由狀態(tài)下,為了討論及的原理,實(shí)際的操作時(shí)序也有可能變化。 AQS Condition 最近面試被問(wèn)到j(luò)ava concurrent包下有哪些熟悉的,用過(guò)的工具。因此來(lái)回顧一下,這些工具的底層實(shí)現(xiàn),AbstractQueuedSynchronizer。在網(wǎng)上看到了其他人的一些技術(shù)博客...
閱讀 2067·2021-11-11 16:55
閱讀 1401·2021-09-28 09:36
閱讀 1047·2019-08-29 15:21
閱讀 1578·2019-08-29 14:10
閱讀 2764·2019-08-29 14:08
閱讀 1638·2019-08-29 12:31
閱讀 3251·2019-08-29 12:31
閱讀 983·2019-08-26 16:47