日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

AbstractQueuedSynchronizer 原理分析 - Condition 实现原理

發(fā)布時(shí)間:2025/3/21 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AbstractQueuedSynchronizer 原理分析 - Condition 实现原理 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

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)景,如下:

  • 時(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
    • 本文鏈接:?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)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 亚洲av男人的天堂在线观看 | 小柔的裸露日记h | 欧美视频免费在线 | 午夜宫| 可以直接看的无码av | 大战熟女丰满人妻av | 亚洲图片中文字幕 | 永久在线免费观看 | 久久夜精 | 欧美国产视频 | 亚洲成人视屏 | 国产富婆一区二区三区 | 好好热视频| 欧美成人免费看 | 高清无码视频直接看 | 免费jizz | 午夜寂寞自拍 | 亚洲国产二区 | 日韩国产第一页 | jizz成人 | 亚洲天堂网一区 | 手机福利视频 | 国产精品日韩专区 | 中文字幕在线日本 | 一区二区三区欧美在线 | 91精品国产色综合久久不卡粉嫩 | 红桃成人网 | 狠狠gao | 日韩毛片中文字幕 | 日韩aaa| 亚洲熟女乱色一区二区三区 | 啪网址 | 天堂无乱码 | 热热色国产 | 国产精品精品视频 | 嫩草嫩草嫩草嫩草 | 一级黄色片毛片 | 亚洲天堂av免费在线观看 | 丰满少妇一区二区三区专区 | 日韩色视频在线观看 | 国产传媒av | av黄色免费 | 久久免费视频一区 | 曰女同女同中文字幕 | 小视频在线免费观看 | 国产在线拍揄自揄拍无码 | 欧美日本高清 | 国产欧美日韩精品一区二区三区 | aaaaa级少妇高潮大片免费看 | 少妇一晚三次一区二区三区 | 91精品观看 | 国产日韩一区 | 玖玖伊人| 亚洲第一区视频 | 黄色大片免费网站 | 69xxx免费视频| 国产成人自拍网站 | 丝袜老师办公室里做好紧好爽 | 男人av资源| 99热都是精品| 国产亚洲女人久久久久毛片 | 久久中文字幕在线观看 | 亚洲va韩国va欧美va | 香蕉伊人| 99riav国产| 久久久久久久久久久电影 | 国产精品美女在线观看 | 91精品国产91久久久久久 | 美女毛片视频 | 婷婷色六月 | 91视频www| 黄色片a级| 日韩一级片免费在线观看 | 日本精品在线 | 丰满少妇xbxb毛片日本 | 人成精品| 尤物精品视频在线观看 | 91香蕉国产在线观看软件 | 日韩在线观看中文字幕 | 国内福利视频 | 久草午夜| 日本久久久久 | 91麻豆蜜桃一区二区三区 | 一区二区高潮 | 午夜插插插 | av色欲无码人妻中文字幕 | 免费成人在线看 | 日韩久久精品电影 | 色一情一交一乱一区二区三区 | 99热这里只有精品9 日韩综合在线 | 欧美精品免费视频 | 国产精品乱码久久久 | 国产精品白虎 | 亚洲天天视频 | 中文无码一区二区三区在线观看 | 天天色影院 | 天天综合色网 | 天天射天天干天天舔 | 伊人午夜|