中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

玉溪網(wǎng)絡推廣 網(wǎng)站建設國外網(wǎng)站加速

玉溪網(wǎng)絡推廣 網(wǎng)站建設,國外網(wǎng)站加速,做寢室介紹網(wǎng)站,企業(yè)微信公眾號平臺官網(wǎng)前言 相關系列 《Java & Lock & 目錄》(持續(xù)更新)《Java & Lock & ConditionObject & 源碼》(學習過程/多有漏誤/僅作參考/不再更新)《Java & Lock & ConditionObject & 總結》(學習…

前言


?相關系列

  • 《Java & Lock & 目錄》(持續(xù)更新)
  • 《Java & Lock & ConditionObject & 源碼》(學習過程/多有漏誤/僅作參考/不再更新)
  • 《Java & Lock & ConditionObject & 總結》(學習總結/最新最準/持續(xù)更新)
  • 《Java & Lock & ConditionObject & 問題》(學習解答/持續(xù)更新)
    ?

?涉及內容

  • 《Java & Lock & 總結》
  • 《Java & Lock & Condition & 總結》
  • 《Java & Lock & LockSupport & 總結》
    ?
    ?

概述


?簡介

????ConditionObject @ 條件對象類用于管理AQS訪問線程。條件對象類是AQS類對條件接口的內部實現(xiàn),同時也是Java JDK對條件接口的唯一實現(xiàn)。條件接口被Java設計用來定義條件機制,條件機制的本質是線程管理機制,用于在并發(fā)環(huán)境下實現(xiàn)對線程的有序協(xié)調及精確控制,即令線程有序的等待/喚醒。AQS類通過對條件機制的引入/實現(xiàn)對外提供了一套可控制自身訪問線程的基礎手段,使得開發(fā)者可以根據(jù)不同需求/環(huán)境/規(guī)則對AQS訪問線程進行邏輯控制。

/*** Condition implementation for a {@link AbstractQueuedSynchronizer} serving as the basis of a {@link Lock}* implementation.* AQS的條件實現(xiàn)用于充當鎖實現(xiàn)的基礎。* <p>* Method documentation for this class describes mechanics, not behavioral specifications from the point of view of* Lock and Condition users. Exported versions of this class will in general need to be accompanied by documentation* describing condition semantics that rely on those of the associated {@code AbstractQueuedSynchronizer}.* 該類的方法文檔從鎖和條件用戶的角度描述了機制,而不是行為規(guī)范。這個類的導出版本通常需要附帶描述依賴于相* 關AbstractQueuedSynchronizer的條件語義的文檔。* <p>* This class is Serializable, but all fields are transient, so deserialized conditions have no waiters.* 該類是序列化的,但所有字段被修飾了transient,所以反序列化的條件沒有等待者。*/
public class ConditionObject implements Condition, Serializable {...
}

????條件對象類用于對暫時失去同步意愿的線程進行管理。我們可能會對“條件對象類用于管理AQS訪問線程”這一說法產生疑惑,因為AQS類本身就是一套協(xié)調/控制線程的線程管理機制,為什么還要在其基礎上額外引入條件機制呢?這是因為AQS類專用于對有同步意愿的線程進行管理,即其只能管理想要/已被同步的線程。但實際情況是線程可能在某些情況下暫時失去同步意愿,但又因為會被恢復而繼續(xù)對AQS保持訪問狀態(tài),從而使得AQS類無法管理此類線程,因此才需要引入/實現(xiàn)條件機制對之進行管理。

????線程暫時失去同步意愿的實際場景有且僅有暫時解除同步一種,相應場景如下:

具有同步意愿:線程一/多次通過AQS達成同步/獲取[state @ 狀態(tài)];
失去同步意愿:線程在同步后執(zhí)行邏輯,后因條件未達成而暫停并暫時解除同步/徹底釋放[狀態(tài)],隨后被安排進入條件對象中等待恢復同步;
恢復同步意愿:線程因為條件達成而被喚醒并恢復同步/一次性獲取釋放的所有[狀態(tài)],隨后從邏輯暫停處繼續(xù)執(zhí)行邏輯。

????條件對象類只支持對獨占線程進行管理。通過上文所述場景我們可知線程為了等待某些條件達成會暫時解除同步,但與此同時我們可能產生新的疑惑:等待條件達成與暫時解除同步之間存在何種聯(lián)系?為什么線程要在等待的過程中暫時解除同步呢?這是因為條件的達成可能需要由其它線程在同步時實現(xiàn),因此如果在該情況下獨占線程不暫時解除同步,則其將因為條件無法達成而永久等待。暫時解除同步后的獨占線程會交由條件對象負責管理(等待/喚醒)。那為什么說條件對象類只支持對獨占線程進行管理呢?這是因為對于共享線程來說,由于設計上允許其它共享線程并行同步的原因,條件完全可以在不解除同步的情況下達成,因此條件對象類不支持也不需要對共享線程進行管理。

????條件對象類是非靜態(tài)的公共類。條件對象類是公共類意味著開發(fā)者可以通過常規(guī)的new關鍵字創(chuàng)建條件對象,而非靜態(tài)則是一個需要特別注意的點,因為這意味著條件對象并非與AQS類關聯(lián)而是與具體的AQS關聯(lián),而這就側面表示了條件對象類對獨占線程的管理是分批而非統(tǒng)一的,即每個條件對象只能管理自身所屬AQS的獨占線程而無法對其它AQS的獨占線程進行管理。
?
?

方法


?等待

  • public final void await() throws InterruptedException —— 等待 —— 令已獨占同步當前AQS的當前線程原子性地解除同步并在當前條件對象中進入等待狀態(tài)。該方法是等待方法“阻塞”形式的實現(xiàn),當前線程會無限等待至因為信號而喚醒為止并重新恢復對當前AQS的獨占同步至初始狀態(tài)。當前線程在進入方法/等待恢復期間如果已/被中斷會拋出中斷異常,但中斷狀態(tài)會被清除。

  • public final void awaitUninterruptibly() —— 等待(不可中斷) —— 令已獨占同步當前AQS的當前線程原子性地解除同步并在當前條件對象中進入等待狀態(tài)。該方法是等待方法“阻塞不可中斷”形式的實現(xiàn),當前線程會無限等待至因為信號而喚醒為止并重新恢復對當前AQS的獨占同步至初始狀態(tài)。當前線程在進入方法/等待恢復期間如果已/被中斷不會拋出中斷異常,但中斷狀態(tài)會被保留。

  • public final boolean await(long time, TimeUnit unit) throws InterruptedException —— 等待 —— 令已獨占同步當前AQS的當前線程原子性地解除同步并在當前條件對象中進入等待狀態(tài)。該方法是等待方法“超時”形式的實現(xiàn)之一,當前線程會有限等待至因為信號而喚醒為止并在重新恢復對當前AQS的獨占同步至初始狀態(tài)后返回true,超時指定等待時間則返回false。當前線程在進入方法/等待恢復期間如果已/被中斷會拋出中斷異常,但中斷狀態(tài)會被清除。

  • public final long awaitNanos(long nanosTimeout) throws InterruptedException —— 等待納秒 —— 令已獨占同步當前AQS的當前線程原子性地解除同步并在當前條件對象中進入等待狀態(tài)。該方法是等待方法“超時”形式的實現(xiàn)之一,當前線程會有限等待至因為信號而喚醒為止并在重新恢復對當前AQS的獨占同步至初始狀態(tài)后返回剩余等待納秒,超時指定等待時間則返回0或負數(shù)。當前線程在進入方法/等待恢復期間如果已/被中斷會拋出中斷異常,但中斷狀態(tài)會被清除。

  • public final boolean awaitUntil(Date deadline) throws InterruptedException —— 等待 —— 令已獨占同步當前AQS的當前線程原子性地解除同步并在當前條件對象中進入等待狀態(tài)。該方法是等待方法“超時”形式的實現(xiàn)之一,當前線程會有限等待至因為信號而喚醒為止并在重新恢復對當前AQS的獨占同步至初始狀態(tài)后返回true,超時指定等待時間則返回false。當前線程在進入方法/等待恢復期間如果已/被中斷會拋出中斷異常,但中斷狀態(tài)會被清除。
    ?

?信號

  • public void signal() —— 信號 —— 喚醒一條在當前條件對象中等待的線程以令之重新恢復對當前AQS的獨占持有。

  • public void signalAll() —— 信號全部 —— 喚醒所有在當前條件對象中等待的線程以令之重新恢復對當前AQS的獨占持有。
    ?
    ?

實現(xiàn)


?條件隊列

在這里插入圖片描述
????條件對象類采用條件隊列管理暫時解除同步的獨占線程。出于公平喚醒及共用節(jié)點類等原因,條件對象類采用與AQS類相似的隊列結構來管理獨占線程,該隊列被稱為條件隊列。條件隊列與同步隊列一樣都是邏輯隊列,條件對象類使用[firstWaiter/lastWaiter @ 首個等待者/最后等待者]來持有條件隊列頭/尾獨占節(jié)點的引用,從而變相持有整個條件隊列。雖說與同步隊列共用節(jié)點類,但條件隊列并不使用[前驅/后繼節(jié)點]鏈接獨占節(jié)點:一是因為條件隊列是單向鏈表,獨占節(jié)點并不需要持有前驅引用;二是因為獨占節(jié)點存在同時位于同步/條件隊列的情況,因此為了支持該情況也必須使用不同的字段來持有其在兩類隊列中的后繼引用。該知識點在AQS類的相應文章中已經提及過,此處再進行詳細闡述:獨占節(jié)點使用[nextWaiter @ 模式/后繼等待者]持有其在條件隊列中的后繼引用,而由于只有獨占節(jié)點才可能加入條件隊列,因此該使用方式并不會與[模式/后繼等待者]原本表示模式的功能設計產生沖突。

????獨占節(jié)點的[模式/后繼等待者]未必一定為<EXCLUSIVE @ 獨占 @ null>。AQS類的文章在講解節(jié)點時說過AQS類使用<獨占>和<SHARED @ 獨占 @ node>兩個靜態(tài)常量節(jié)點作為表示獨占/共享模式的標記,因此當[模式/后繼等待者]為<獨占>時便意味著其為獨占節(jié)點…但實際上該說法并不準確…因為就在剛才我們提到了獨占節(jié)點存在同時位于同步/條件隊列的情況,而又因為獨占節(jié)點使用[模式/后繼等待者]持有其在條件隊列中的后繼引用,因此[模式/后繼等待者]也完全可以在同步隊列中持有具體引用。那能否說[模式/后繼等待者]為<獨占>就表示獨占節(jié)點只位于同步隊列中呢?也不可以…因為當獨占節(jié)點恰好為條件隊列的尾節(jié)點時其[模式/后繼等待者]也一樣為<獨占>,因此想要準確判斷節(jié)點是否為獨占模式實際上只能通過查看[模式/后繼等待者]是否不為<共享>判斷。

????一個AQS可能關聯(lián)多個條件隊列。與同步隊列只有一個不同,一個AQS可能關聯(lián)了多個條件隊列。這并不是說條件隊列可以在條件對象中存在多個,而是AQS類允許基于一個AQS創(chuàng)建多個條件對象。該特性使得開發(fā)者可以進一步細化對獨占線程的管理,即即使是同屬于一個AQS的獨占線程也可以通過調用不同條件對象來進行分批管理,只要條件對象基于該AQS創(chuàng)建即可。
?

?等待/暫時解除同步

????條件對象類將線程同步的暫時解除與等待進行了合并。AQS類通過條件對象類的等待方法令獨占線程暫時解除同步,即當獨占線程調用條件對象的等待方法時會同時完成暫時解除同步及等待兩項功能。因此條件對象類不單單只是負責對獨占線程的管理,還是令獨占線程暫時解除同步的操作入口。

????獨占線程會自封為獨占節(jié)點尾插至條件隊列中。當獨占線程調用等待方法暫時解除同步時,其會將自身封裝為[waitStatus @ 等待狀態(tài)]為<CONDITION @ -2 @ 條件>的獨占節(jié)點并尾插至條件隊列中。整個尾插過程并不需要CAS的保護,因為此時的獨占線程尚未解除同步,故而在此期間不會有其它獨占線程參與競爭而導致線程安全問題。那是否存在獨占節(jié)點直接從同步隊列遷移至條件隊列的情況呢?這個問題確實值得思考,因為獨占線程在其位于同步隊列等待同步的過程中暫時失去同步意愿似乎也是有可能的…但很遺憾這種情況并不存在。上文已經說過,目前關于獨占線程暫時失去同步意愿的具體場景只存在暫時解除同步一種,而對于已同步的獨占線程來說即使其曾經確實被安排加入過同步隊列以等待同步,也會在成功同步后斷開與其所屬獨占節(jié)點的關聯(lián)。因此即使其所屬獨占節(jié)點依舊以[head @ 頭節(jié)點]的形式存在于同步隊列中,獨占線程也必然已從同步隊列中脫離,故而加入條件隊列的獨占節(jié)點必然是全新創(chuàng)建的。

????獨占線程可能在加入條件隊列前觸發(fā)對條件隊列的清理。在將自身封裝為獨占節(jié)點并加入條件隊列之前,獨占線程會先判斷尾節(jié)點是否存在且是否為取消節(jié)點,是則觸發(fā)對條件隊列的清理。該知識點會在下文講解取消時詳述,此處之所以會特別提及源于該觸發(fā)時機屬于“彌補”性質,即專用于處理執(zhí)行清理的獨占線程在某些情況下無法判斷獨占節(jié)點是否位于條件隊列中的情況。

????獨占線程會在成功加入條件隊列后暫時解除同步。暫時解除同步的本質是徹底釋放所有[狀態(tài)]并喚醒同步隊列的首個非空節(jié)點[線程],這里強調徹底是因為獨占線程可能因為重入而經歷過多次同步。但無論其達成同步/獲取[狀態(tài)]多少次在暫時解除同步時都會被一次性釋放,以使得其它獨占線程獲得被同步的可能。暫時解除同步可能失敗,原因是其底層調用的tryRelease(int arg)方法由AQS類子類負責實現(xiàn),因此無法保證[狀態(tài)]徹底釋放的必然成功。條件隊列中獨占線程會在暫時解除同步失敗(理論上不允許失敗)時拋出非法監(jiān)視器狀態(tài)異常,但獨占節(jié)點則會在[等待狀態(tài)]會被賦值為<CANCELLED @ 1 @ 取消>后繼續(xù)保留在條件隊列中等待被清理,這也是獨占節(jié)點在條件隊列中唯一會被顯式取消的情況。

????獨占線程會在暫時解除同步后進入有限/無限等待狀態(tài)。獨占線程進入有限/無限等待狀態(tài)的本質是LockSupport.park(…)方法的自我調用,該內容的關鍵在于無論何種形式的等待方法都必須以循環(huán)的方式調用LockSupport.park(…)方法以滿足“實現(xiàn)需求設計”與“避免虛假喚醒”兩方面的要求。以“實現(xiàn)需求設計”舉例:對于不可中斷形式的等待方法而言,如果獨占線程被中斷喚醒,則獨占線程可以通過循環(huán)再次進入有限/無限等待狀態(tài)以避免在自身條件未達成時恢復同步。條件對象類將“獨占節(jié)點不位于同步隊列中”作為等待循環(huán)的判斷條件,原因是將獨占節(jié)點從條件隊列遷移至同步隊列只會在符合需求設計的喚醒前/后發(fā)生,例如條件達成時的信號喚醒前;又例如可中斷/定時的等待方法中發(fā)生的中斷/超時喚醒后等,因此獨占節(jié)點位于同步隊列中便意味著獨占線程已無需/不許再繼續(xù)等待,從而退出等待循環(huán)。

????“避免虛假喚醒”也是等待必須循環(huán)執(zhí)行的必要理由。虛假喚醒是一種發(fā)生于LockSupport.park(…)方法的奇妙現(xiàn)象,具體表現(xiàn)為因LockSupport.park(…)方法而進入有限/無限等待狀態(tài)的線程可能在毫無理由的情況下被喚醒。虛假喚醒并不屬于符合需求設計的喚醒,因此其發(fā)生前/后都不應該遷移獨占節(jié)點,故而如果不輔以循環(huán),獨占線程將可能在不符合業(yè)務的情況下提前結束等待,而這顯然是不允許的。事實上由于虛假喚醒這一奇妙現(xiàn)象的存在,將LockSupport.park(…)方法置于相應邏輯的循環(huán)中執(zhí)行才是官方指定的正確調用方式。

????判斷獨占節(jié)點是否位于同步隊列中并不十分耗時。條件對象類通過調用AQS類的isOnSyncQueue(Node node)方法判斷獨占節(jié)點是否位于同步隊列中,而其操作本質是以[尾節(jié)點]/獨占節(jié)點為起/終點的前遍歷查找行為。當獨占節(jié)點已遷移至同步隊列中時,由于同步隊列是尾插法,又因為遷移與喚醒(無論兩者前后順序如何)是緊密相連的操作/時間間隔非常短,因此在獨占線程前遍歷查找時獨占節(jié)點通常就位于同步隊列的尾部附近,故而并不十分耗時。那如果獨占節(jié)點尚未被遷移至同步隊列中呢?豈不是要進行全遍歷?這難道還不耗時么?的確按正常邏輯來說如果在同步隊列中等待同步的線程非常多的話那確實將是一項非常耗時的操作…但這并非是無法彌補的。AQS類采用了多項優(yōu)化方案避免獨占線程在查找所屬獨占節(jié)點時全遍歷同步隊列,這些優(yōu)化方案可以對獨占節(jié)點位于/不位于同步隊列的大部分情況進行快速篩選,從而使得全遍歷行為只在少數(shù)極端條件下執(zhí)行…優(yōu)化策略具體如下:

判斷獨占節(jié)點的[等待狀態(tài)]是否為<條件> —— <條件>是獨占節(jié)點"只"位于條件隊列中時才可能存在的狀態(tài),當獨占節(jié)點被遷移至同步隊列時其[等待狀態(tài)]會被賦值為0,因此如果發(fā)現(xiàn)獨占節(jié)點的[等待狀態(tài)]為<條件>便意味著其必然不位于同步隊列中;

判斷獨占節(jié)點的[前驅節(jié)點]是否為null —— 獨占節(jié)點的[前驅節(jié)點]為null意味著獨占節(jié)點不位于或以[頭節(jié)點]的形式存在于同步隊列中。由于此時的獨占線程尚未恢復同步,因此其所屬獨占節(jié)點不可能成為[頭節(jié)點],因此如果獨占節(jié)點的[前驅節(jié)點]為null便意味著其必然不位于同步隊列中;

判斷獨占節(jié)點的[后繼節(jié)點]是否不為null —— 獨占節(jié)點的[后繼節(jié)點]為null并不代表其一定不位于同步隊列中,因為同步隊列[尾節(jié)點][后繼節(jié)點]也為null,但如果獨占節(jié)點的[后繼節(jié)點]不為null便意味著其必然位于同步隊列中。
?

?信號喚醒

????在條件隊列中有限/無限等待的獨占線程會因為信號/中斷/超時/虛假四種原因被喚醒。我們已知虛假喚醒是需求設計所不允許的特殊情況,并也已通過等待循環(huán)的方式彌補,因此關于喚醒的核心內容會集中在其它三種喚醒操作上。事實上這三種喚醒操作單獨講解起來都并不復雜,但關鍵在于三者是允許交錯并發(fā)的,這就要求條件對象類必須能夠在復雜的并發(fā)場景中準確查明/分辨出獨占線程被喚醒的真實原因以對外作出正確反饋,而這才是喚醒操作的真正難點所在。事實上想要判斷得知獨占線程被喚醒的真實原因是難以/無法做到的,原因是缺乏可靠的判斷依據(jù),因此條件對象類采用“標記”方案定性獨占線程的喚醒方式?!皹擞洝狈桨傅暮诵臑閾屨?#xff0c;即當多種喚醒操作并發(fā)時獨占線程會被定性為被優(yōu)先標記的喚醒方式喚醒,因此獨占線程的實際喚醒方式與標記喚醒方式可能并不相同,該知識點會在下文講解三種喚醒操作時詳述。

????“信號喚醒”只能由正在同步的獨占線程執(zhí)行。成功同步的獨占線程可以通過調用信號方法中的signal()方法喚醒在條件隊列中等待的首個獨占節(jié)點[thread @ 線程],此外其還可以調用signalAll()方法喚醒在條件隊列中等待的所有獨占線程。signalAll()方法會遍歷條件隊列并依次喚醒所有的獨占線程,這在效果上與循環(huán)調用signal()方法是等價的,但避免了一些非必要的操作。

????“信號喚醒”可能因為失敗而重復執(zhí)行?!靶盘枂拘选笔〉脑蚴且驗楠氄季€程已被并發(fā)標記為中斷/超時喚醒,由于喚醒方法存在必須令等待中的獨占線程會被信號喚醒的定義,因此失敗時signal()方法會重新定位新首個獨占線程以再次執(zhí)行“信號喚醒”,直至喚醒成功或無首個獨占節(jié)點[線程]可被喚醒為止。

????“信號喚醒”會嘗試將獨占節(jié)點的[等待狀態(tài)]由<條件>CAS賦值為0,該CAS被稱為標記CAS。標記CAS并非只是單純的CAS賦值,而是所有喚醒操作都必須執(zhí)行的關鍵步驟,其作用具體有四:一是將[等待狀態(tài)]還原為初始狀態(tài)0以備獨占節(jié)點加入同步隊列;二是將獨占線程的喚醒方式標記為自身;三是判斷獨占節(jié)點是否已被取消;四是判斷獨占線程是否已被標記為被其它喚醒方式喚醒。

????獨占節(jié)點最初會以<條件>的[等待狀態(tài)]加入條件隊列,又因為[等待狀態(tài)]會在獨占線程暫時解除同步失敗時被顯式賦值為<取消>,因此標記CAS失敗就意味著獨占節(jié)點可能已被取消;此外由于所有的喚醒都會執(zhí)行標記CAS,因此失敗還意味著獨占線程可能已被標記為被其它喚醒方式喚醒;最后如果標記CAS成功,則意味著獨占線程已被標記被當前喚醒方式喚醒,因為其它喚醒操作的標記CAS會因為當前喚醒操作的標記CAS成功而失敗。但請注意!獨占線程被標記為指定的喚醒只是近似的模擬,是因為無法準確探知而選取的最大可能,并不意味著獨占線程實際就是被標記的喚醒方式所喚醒…例如下表中就展示了獨占線程被中斷/超時喚醒但又被標記為信號喚醒的情況:

信號喚醒中斷/超時喚醒
T01獨占線程因為中斷/超時而喚醒
T02將獨占節(jié)點[等待狀態(tài)]由<條件>CAS賦值為0成功,獨占線程被標記為信號喚醒
T03將獨占節(jié)點[等待狀態(tài)]由<條件>CAS賦值為0失敗,認定獨占線程已被信號喚醒

????被成功標記CAS的獨占節(jié)點會被遷移至同步隊列以重新競爭同步/恢復同步意愿。被標記為信號喚醒的獨占線程會將所屬獨占節(jié)點從條件隊列中遷移至同步隊列,即獨占節(jié)點會從條件隊列中頭部移除并尾插至同步隊列中。獨占節(jié)點被遷移至同步隊列意味著獨占線程重新參與了對同步的競爭,并由此宣告其正式恢復同步意愿。需要重點提及是:由于獨占節(jié)點在加入同步隊列前會從條件隊列的頭部移除,即會將[模式/后繼等待者]置null以斷開與條件隊列的關聯(lián),因此“信號喚醒”中被遷移的獨占節(jié)點只存在于同步隊列中。但除此以外,被遷移的獨占節(jié)點還有同時存在于同步/條件隊列的情況,該知識點會在下文講解中斷/超時喚醒時詳述。

????“信號喚醒”的核心是“遷移”而非“喚醒”,其只保證獨占線程必然會被喚醒,但不保證由執(zhí)行“信號喚醒”的同步獨占線程執(zhí)行。獨占節(jié)點被遷移至同步隊列后,此時的獨占線程依然處于有限/無限等待狀態(tài),并且未必會被立即信號喚醒,即“信號喚醒”并非一定會對獨占線程執(zhí)行LockSupport.unpark(Thread thread)方法。之所以會如此是因為獨占節(jié)點加入同步隊列后獨占線程為了等待同步也需要進入有限/無限等待狀態(tài),因此“信號喚醒”通常不需要將其喚醒,只需放任其等待“狀態(tài)喚醒”即可。當然情況也并非完全如此,在兩種情況下“信號喚醒”也需要喚醒獨占線程:一是獨占節(jié)點的[前驅節(jié)點]為取消節(jié)點,這是出于令獨占線程執(zhí)行“等待 - 前驅式清理”的目的;二是在“狀態(tài)喚醒”執(zhí)行線程將獨占節(jié)點[前驅節(jié)點]的[等待狀態(tài)]由0CAS賦值為<SIGNAL @ -1 @ 信號>失敗時,因為這可能導致獨占線程永遠無法被喚醒,是與取消CAS相同性質的行為,因此需要將之信號喚醒以執(zhí)行“等待 - 等待CAS”操作。
?

?中斷/超時喚醒

????被喚醒的獨占線程會第一時間判斷自身是否已被中斷。無論實際喚醒方式及等待方法的形式為何,獨占線程被喚醒后都會第一時間通過響應中斷的方式判斷自身是否已被中斷,這么做的目的是確定自身是否具有被中斷喚醒的可能,并以此為依據(jù)執(zhí)行“中斷喚醒”,故而“中斷喚醒”的執(zhí)行者實際上是被喚醒的獨占線程本身。

????“中斷喚醒”會執(zhí)行標記CAS。當標記CAS成功時,被標記為中斷喚醒的獨占線程會遷移獨占節(jié)點至同步隊列中以重新競爭同步/恢復同步意愿,因此與“信號喚醒”不同,“中斷喚醒”中的獨占線程是以喚醒的形態(tài)被遷移至同步隊列的,并且遷移也位于獨占線程被喚醒之后。此外需要特別注意的是:“中斷喚醒”在遷移獨占節(jié)點時并不會將其從條件隊列中移除,原因是該獨占節(jié)點未必與“信號喚醒”中的獨占節(jié)點一樣是條件隊列的頭節(jié)點,即不一定是[首個等待者],因此想要移除就必須先對條件隊列進行遍歷查找,而這顯然是不高效也不安全的行為。因為這即可能導致頻繁的長遍歷,又可能因為多獨占線程并發(fā)中斷遷移而導致部分獨占節(jié)點被遺留在條件隊列中…因此也就直接導致了獨占節(jié)點同時位于同步/條件隊列情況的產生。

在這里插入圖片描述

????“中斷喚醒”的標記CAS失敗意味著獨占線程已被并發(fā)標記為信號喚醒,在這種實際被中斷喚醒但又被標記為信號喚醒的情況下獨占線程會額外判斷/保證獨占節(jié)點在自身退出等待循環(huán)前必然位于同步隊列中。之所以要進行額外判斷/保證是因為獨占線程會在確認自身被中斷后不經下次的等待判斷而直接退出當前等待循環(huán),而又因為“信號喚醒”對獨占節(jié)點的遷移位于標記CAS之后,因此此時的獨占節(jié)點[等待狀態(tài)]雖然已被CAS賦值為0,但可能尚未被遷移至同步隊列中。而由于獨占線程正處于喚醒狀態(tài),因此當其退出等待循環(huán)并嘗試獲取[狀態(tài)]時將可能因為獨占節(jié)點不位于同步隊列/沒有[前驅節(jié)點]而拋出NullPointerException @ 空指針異?!嚓P源碼及模擬異常場景如下:

while (!isOnSyncQueue(node)) {LockSupport.park(this);// ---- 獨占線程在確定自身被中斷后會直接退出等到循環(huán),因此checkInterruptWhileWaiting方法中包含了響應中斷和遷移保障的邏輯。if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;
}
信號喚醒/同步線程中斷/獨占線程
T01因為中斷而被喚醒,并發(fā)現(xiàn)自身被中斷
T02執(zhí)行標記CAS成功執(zhí)行標記CAS失敗
T03直接退出等待循環(huán)
T04嘗試獲取[狀態(tài)]并因為獨占節(jié)點不位于同步隊列/沒有[前驅節(jié)點]而拋出空指針異常
T05將獨占節(jié)點從條件隊列遷移至同步隊列

????獨占線程通過yield()方法保證所屬獨占節(jié)點在其退出等待循環(huán)前必然位于同步隊列中。當獨占線程在額外判斷中發(fā)現(xiàn)獨占節(jié)點未位于同步隊列時會短暫放棄CPU資源以期望自身再次獲取CPU資源時獨占節(jié)點已加入同步隊列,由于該行為無法保證必然性,因此yield()方法同樣需要循環(huán)執(zhí)行。

while (!isOnSyncQueue(node))Thread.yield();

????在定時形式的等待方法中如果獨占線程被喚醒后發(fā)現(xiàn)自身沒有被標記為中斷/信號喚醒,則會在下個循環(huán)中判斷自身是否有被超時喚醒的可能。獨占線程通過查看是否存在剩余等待時間判斷其是否可能被超時喚醒,這種可能在剩余等待時間不存在時成立,因此該情況下獨占線程會執(zhí)行“超時喚醒”…關鍵源碼如下:

while (!isOnSyncQueue(node)) {// ---- 判斷是否已經超時,超時的優(yōu)先級比等待和中斷更高,因此被超時喚醒的獨占線程只// 能在下次循環(huán)中判斷超時。而在這個過程中獨占線程可能被標記為信號/中斷喚醒。if (nanosTimeout <= 0L) {timedout = transferAfterCancelledWait(node);break;}// ---- 在等待時間剩余較多的情況下等待,否則直接自旋。if (nanosTimeout >= spinForTimeoutThreshold)LockSupport.parkNanos(this, nanosTimeout);// ---- 獨占線程在確定自身被中斷后會直接退出等到循環(huán)。if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;nanosTimeout = deadline - System.nanoTime();
}

????“超時喚醒”與“中斷喚醒”在流程上是相同的?!俺瑫r”及“中斷”兩種喚醒操作底層都調用transferAfterCancelledWait(Node node)方法實現(xiàn),該方法用于執(zhí)行標記CAS,并在成功后將獨占節(jié)點遷移至同步隊列,或在失敗后判斷/保證獨占線程在退出等待循環(huán)前令獨占節(jié)點位于同步隊列中。兩者的區(qū)別在于“超時喚醒”會直接調用該方法,而“中斷喚醒”則會在checkInterruptWhileWaiting(Node node)方法中確認獨占線程被中斷后再間接調用該方法。從上述源碼中我們可以發(fā)現(xiàn)由于“超時喚醒”與“中斷喚醒”會先后執(zhí)行,因此對于一個獨占線程而言雖然中斷/超時喚醒之間確實可能并發(fā),但兩套操作卻不存在并發(fā)情況。而對于“信號喚醒”而言其雖然知道標記CAS失敗是因為“中斷/超時喚醒”的原因,但卻不知道也沒必要知道具體是兩者中的哪一個所導致的。
?

?恢復同步

????被喚醒的獨占線程會恢復同步意愿。我們已知“信號/中斷/超時喚醒”會將獨占節(jié)點遷移至同步隊列中以令之重新競爭同步,由此宣告獨占線程正式恢復同步意愿。重新競爭同步與常規(guī)競爭同步最大的區(qū)別在于其會一次性獲取之前釋放的所有[狀態(tài)],從而將獨占線程的同步恢復到與暫時解除同步前完全一致的情況。此外重新競爭同步不會中斷/超時,無論獨占線程最初是否調用了可中斷/定時形式的獲取方法來獲取同步,在重新競爭時都不會拋出中斷異常或放棄同步,因為中斷/超時只作用于獨占線程首次達成同步的時候,該設計保證了獨占線程的同步必然可被恢復。

????被標記為中斷喚醒的獨占線程會在恢復同步后拋出中斷異常。當獨占線程再次經歷競爭而恢復同步后,如果其被標記為中斷喚醒,并且等待方法的形式允許中斷,則其會直接拋出中斷異常。一個值得思考的問題是:為什么獨占線程不直接在喚醒時拋出中斷異常而是在恢復同步后再拋出呢?實際上這是為了保證獨占線程同步的必然恢復,以維護獨占線程對[狀態(tài)]的獲取/釋放必須成對出現(xiàn)的使用設定。事實上對于拋出中斷異常該行為可中斷形式的等待方法只允許兩種情況:一是在獨占線程尚未暫時解除同步時發(fā)生;二是在獨占線程恢復同步后發(fā)生。該兩者的核心都在于必須保證中斷異常拋出時獨占線程處于同步狀態(tài)。

????被中斷但未被標記為中斷喚醒的獨占線程不會拋出中斷異常,但中斷狀態(tài)會被保留。在獨占線程被實際中斷喚醒但未被標記中斷喚醒的情況下,無論并發(fā)場景為何,中斷都會被統(tǒng)一視作在獨占線程被喚醒后產生。而由于獨占線程未被標記為中斷喚醒,因此該情況在需求設計上不滿足中斷異常的拋出條件,只會保留中斷狀態(tài)以表示獨占線程遭遇過中斷。此外如果等待方法的形式不允許中斷,則即使獨占線程被標記為中斷喚醒也只會保留中斷狀態(tài)而不會拋出中斷異常。需要注意的是:被保留的中斷狀態(tài)屬于重中斷,是等待方法為了保留中斷狀態(tài)而額外執(zhí)行的中斷,原因是獨占線程無論是在暫時解除同步后的條件隊列中被中斷還是在恢復同步后的同步隊列中被中斷其中斷狀態(tài)都會因為中斷判斷而被響應消除。為此等待方法會記錄/整合兩個階段的中斷判斷結果為以下三種標記,以作為其選擇中斷處理方案的依據(jù):

<REINTERRUPT @ 1 @ 重中斷>:表示獨占線程在條件隊列被中斷但未被標記為中斷喚醒,以及在同步隊列中被中斷的標記,該標記意味著等待方法在退出時需要重中斷以保留中斷狀態(tài),因為獨占線程原本的中斷狀態(tài)會因為中斷判斷而被響應消除;

<0>:表示獨占線程未被中斷的標識,該標記意味著等待方法在退出時即不需要拋出中斷異常也不需要重中斷以保留中斷狀態(tài);

<THROW_IE @ -1 @ 拋出異常>:表示獨占線程條件隊列中被標記中斷喚醒的標記,該標記意味著等待方法在退出時需要拋出中斷異常。

?

?取消

在這里插入圖片描述

????條件隊列中所有[等待狀態(tài)]不為<條件>的獨占節(jié)點都屬于取消節(jié)點。與同步隊列不同,除[等待狀態(tài)]被顯式賦值為<取消>的獨占節(jié)點屬于取消節(jié)點外,條件隊列中其它[等待狀態(tài)]不為<條件>的獨占節(jié)點也都會被視作隱式取消節(jié)點。由此可知被標記為超時/中斷喚醒的獨占節(jié)點本質上都屬于隱式取消節(jié)點,因為這些獨占節(jié)點在標記CAS后會直接加入同步隊列且不會從條件隊列中移除。而被標記為信號喚醒的獨占節(jié)點由于會真正意義上的“遷移”因此不屬于取消節(jié)點的范圍…雖然其也會因為標記CAS和遷移之間存在時間間隔而短暫同時位于同步/條件隊列中,但由于時間過于短暫因此通常不在意這一點。

????隱式取消節(jié)點[線程]會全局清理條件隊列中的取消節(jié)點。隱式取消節(jié)點[線程]會在其恢復同步后,拋出中斷異常/重中斷之前遍歷性的全局清理條件隊列中的取消節(jié)點,之所以只有隱式取消節(jié)點[線程]會在該時段執(zhí)行清理有兩點原因:一是因為顯式取消節(jié)點[線程]會直接拋出非法監(jiān)視器狀態(tài)異常而無法執(zhí)行;二是如果令顯式取消節(jié)點[線程]也在此時執(zhí)行清理將導致大量的無用功。因為取消節(jié)點通常與隱式取消節(jié)點在數(shù)量上等同,因此令隱式取消節(jié)點[線程]執(zhí)行清理就已足夠/過量滿足清理所需,無需再令顯式取消節(jié)點[線程]也執(zhí)行喚醒??梢园l(fā)現(xiàn)的是我們在進行數(shù)量對比時并沒有考慮顯式取消節(jié)點,因為只要符合調用規(guī)范,暫時解除同步理論上是不應該失敗的,因此顯式取消節(jié)點的產生本質上屬于AQS類子類在實現(xiàn)上的缺陷,故而不納入計算范圍。

????隱式取消節(jié)點[線程]通過判斷[模式/后繼等待者]是否為null決定自身是否執(zhí)行清理。雖說上文在描述時一直使用“被標記為**喚醒的獨占線程”這種說法,但實際上獨占線程并無法明確得知自身被標記的喚醒方式,因為其并沒有以字段的形式被保存下來。這大概率是出于節(jié)約內存方面的考量,因為該數(shù)據(jù)在整體流程中并無作為準確判斷依據(jù)的必要,原因是隱式取消節(jié)點[線程]可以通過查看[模式/后繼等待者]是否為null來代替判斷自身是否被標記為中斷/超時喚醒。隱式取消節(jié)點[線程]會因為其所屬節(jié)點保留在條件隊列中而[模式/后繼等待者]不為null,因此[模式/后繼等待者]不為null便可作為隱式取消節(jié)點[線程]觸發(fā)清理的判斷依據(jù)。但該判斷方案并非是完美的,因為當取消節(jié)點為[最后等待者],即條件隊列的尾節(jié)點時其[模式/后繼等待者]也為null,因此在該情況下隱式取消節(jié)點[線程]對條件隊列的清理將不會被觸發(fā)。

????獨占線程可能在加入條件隊列前觸發(fā)對取消節(jié)點的清理。對于取消節(jié)點為[最后等待者]而無法觸發(fā)清理問題的處理方案其實在上文中已經提及過了,即獨占線程可能在加入條件隊列前觸發(fā)對條件隊列的清理。獨占線程在將自身封裝為獨占節(jié)點并尾插至條件隊列前會先判斷[最后等待者]是否為取消節(jié)點,是則先執(zhí)行清理以彌補上文的情況。我們由此可知獨占線程最多可能執(zhí)行兩次條件隊列清理,并且暫時解除同步失敗的線程也可能執(zhí)行清理,因為其可以在獨占節(jié)點入隊前觸發(fā)…但憑心而論該特殊處理顯得沒那么精妙,因為隱式取消節(jié)點[線程]完全可以在發(fā)現(xiàn)[模式/后繼等待者]為null后再判斷所屬節(jié)點是否為[最后等待者]來執(zhí)行清理,而無需在入隊前判斷來提升代碼的復雜性…個人認為這可能是因為獨占節(jié)點[線程]在入隊時原本就需要獲取[最后等待者]以作為其入隊基礎的原因,畢竟如此一來判斷[最后等待者]是否為取消節(jié)點就成為了順勢行為,從而避免隱式取消節(jié)點[線程]專門執(zhí)行這一點。

????條件隊列的清理是線程安全的,但無法保證取消節(jié)點全移除。無論是獨占線程加入條件隊列前/恢復同步后執(zhí)行的清理都具有一個特點,即獨占線程都處于同步狀態(tài)。這意味著取消節(jié)點的清理是不會出現(xiàn)并發(fā)的,不會因為多獨占線程并發(fā)執(zhí)行而出現(xiàn)取消節(jié)點遺留的問題。但話說如此,取消節(jié)點遺留的現(xiàn)象是依然存在的,因為取消節(jié)點的產生是允許并發(fā)的,畢竟超時/中斷喚醒并不會因為當前有獨占線程持有同步而停止。

http://www.risenshineclean.com/news/41028.html

相關文章:

  • 寧波做日用品外貿公司網(wǎng)站百度搜索引擎原理
  • 湖北網(wǎng)站建設找哪家如何做好營銷推廣
  • asp.net網(wǎng)站安全太原網(wǎng)站制作優(yōu)化seo公司
  • 安順住房和城鄉(xiāng)建設部網(wǎng)站網(wǎng)站app開發(fā)公司
  • 昆山做網(wǎng)站的公司有哪些關鍵詞免費
  • 德陽企業(yè)品牌網(wǎng)站建設seo網(wǎng)站推廣公司
  • 聚美優(yōu)品一個專注于做特價的網(wǎng)站活動推廣軟文
  • 建站系統(tǒng)磁力搜索引擎不死鳥
  • 織夢做網(wǎng)站的教程短視頻代運營合作方案
  • 吃什么補腎虛效果最好食物焦作整站優(yōu)化
  • 2018網(wǎng)站的建設與維護前景網(wǎng)店代運營商
  • 鎮(zhèn)江做網(wǎng)站的做網(wǎng)站用什么編程軟件
  • 科技網(wǎng)站 網(wǎng)站建設廣告公司業(yè)務推廣
  • 網(wǎng)站開發(fā)需解決什么問題廣州百度關鍵詞搜索
  • 用html做班級網(wǎng)站萬網(wǎng)域名注冊信息查詢
  • 有道翻譯網(wǎng)站 做翻譯太原網(wǎng)站推廣排名
  • c做網(wǎng)站百度云登錄
  • wordpress首頁加登錄在線seo短視頻
  • 免費學校網(wǎng)站管理系統(tǒng)南京百度seo排名
  • 有哪些做婚禮電子請柬的網(wǎng)站品牌推廣活動策劃方案
  • 榆林免費做網(wǎng)站推廣引流渠道
  • 如何做分類網(wǎng)站信息營銷市場調研一般怎么做
  • 惠州做網(wǎng)站 百度優(yōu)化線上宣傳渠道和宣傳方式
  • 達州網(wǎng)站建設公司電商引流推廣方法
  • wordpress 子目錄 404西安seo優(yōu)化公司
  • 網(wǎng)上建立網(wǎng)站網(wǎng)絡營銷策略分析方法
  • 網(wǎng)站解析什么意思建網(wǎng)站需要多少錢和什么條件
  • 網(wǎng)站名怎么寫整站優(yōu)化報價
  • 做機械的網(wǎng)站網(wǎng)站人多怎么優(yōu)化
  • 做汽車團購的網(wǎng)站建設直通車推廣計劃方案