<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          我試圖通過這篇文章告訴你,這行源碼有多牛逼.

          共 15037字,需瀏覽 31分鐘

           ·

          2023-06-26 22:27

          你好呀,我是歪歪。

          這次給你盤一個(gè)特別有意思的源碼,正如我標(biāo)題說的那樣:看懂這行源碼之后,我不禁鼓起掌來,直呼祖師爺牛逼。

          這行源碼是這樣的:

          java.util.concurrent.LinkedBlockingQueue#dequeue

          74bdf14743d823b422738992caf58d91.webp

          h.next = h,不過是一個(gè)把下一個(gè)節(jié)點(diǎn)指向自己的動(dòng)作而已。

          這行代碼后面的注釋“help GC”其實(shí)在 JDK 的源碼里面也隨處可見。

          不管怎么看都是一行平平無奇的代碼和隨處可見的注釋而已。

          但是這行代碼背后隱藏的故事,可就太有意思了,真的牛逼,兒豁嘛。

          09a104602a7dac4c12d3efd8e8cd89c4.webp

          它在干啥。

          首先,我們得先知道這行代碼所在的方法是在干啥,然后再去分析這行代碼的作用。

          所以老規(guī)矩,先搞個(gè) Demo 出來跑跑:

          2805eebb8c9dac48ff7e9a213fcb3d30.webp

          在 LinkedBlockingQueue 的 remove 方法中就調(diào)用了 dequeue 方法,調(diào)用鏈路是這樣的:

          19afe0cb0ef252c811621f533455725c.webp

          這個(gè)方法在 remove 的過程中承擔(dān)一個(gè)什么樣的角色呢?

          這個(gè)問題的答案可以在方法的注釋上找到:

          5032a2cd52ab2b6c7d3354c5e14befb8.webp

          這個(gè)方法就是從隊(duì)列的頭部,刪除一個(gè)節(jié)點(diǎn),其他啥也不干。

          就拿 Demo 來說,在執(zhí)行這個(gè)方法之前,我們先看一下當(dāng)前這個(gè)鏈表的情況是怎么樣的:

          5dfba974311af503b93e160791a16af1.webp

          這是一個(gè)單向鏈表,然后 head 結(jié)點(diǎn)里面沒有元素,即 item=null,對(duì)應(yīng)做個(gè)圖出來就是這樣的:

          dbea393645525cf8faf33dd964acc9c6.webp

          當(dāng)執(zhí)行完這個(gè)方法之后,鏈表變成了這樣:

          7415d81e6aa9f4ff9d089db485197cc6.webp

          再對(duì)應(yīng)做個(gè)圖出來,就是這樣的:

          f0b040e1496bc2d3337f599d466fe067.webp

          可以發(fā)現(xiàn) 1 沒了,因?yàn)樗钦嬲摹邦^節(jié)點(diǎn)”,所以被 remove 掉了。

          這個(gè)方法就干了這么一個(gè)事兒。

          雖然它一共也只有六行代碼,但是為了讓你更好的入戲,我決定先給你逐行講解一下這個(gè)方法的代碼,講著講著,你就會(huì)發(fā)現(xiàn),誒,問題它就來了。

          59b6ff0849317653d69d10f6b34bd2ae.webp

          首先,我們回到方法入口處,也就是回到這個(gè)時(shí)候:

          7415d81e6aa9f4ff9d089db485197cc6.webp

          前兩行方法是這樣的:

          bcbd4415c8227d0f2af3ed3071c98dde.webp

          對(duì)應(yīng)到圖上,也就是這樣的:

          • h 對(duì)應(yīng)的是 head 節(jié)點(diǎn)
          • first 對(duì)應(yīng)的是 “1” 節(jié)點(diǎn)
          6a9991baaa5a504d772e923c2b42e670.webp

          然后,來到第三行:

          c203f637f874f4bcb620d0d55b94c76c.webp

          h 的 next 還是 h,這就是一個(gè)自己指向自己的動(dòng)作,對(duì)應(yīng)到圖上是這樣的:

          37dff35d44b648c15db6108a2854f5de.webp

          然后,第四行代碼:

          67958fa357823780237649974e81c917.webp

          把 first 變成 head:

          e36566143bd26a6448b16f4b5bca0023.webp

          最后,第五行和第六行:

          0601bda929d0f011f68908ccb492360c.webp

          拿到 first 的 item 值,作為方法的返回值。然后再把 first 的 item 值設(shè)置為 null。

          對(duì)應(yīng)到圖中就是這樣,第五行的 x 就是 1,第六行執(zhí)行完成之后,圖就變成了這樣:

          9a9e4ede40c3510d11c73c1673367841.webp

          整個(gè)鏈表就變成了這樣:

          8f69a01b053fe522b4fe943da0ac0b81.webp

          那么現(xiàn)在問題來了:

          如果我們沒有 h.next=h 這一行代碼,會(huì)出現(xiàn)什么問題呢?

          我也不知道,但是我們可以推演一下:

          33407d0296bcc5a19620427a8d8b9466.webp

          也就是最終我們得到的是這樣的一個(gè)鏈表:

          104a3715018704dc45da30ea367b38c6.webp

          這個(gè)時(shí)候我們發(fā)現(xiàn),由于 head 指針的位置已經(jīng)發(fā)生了變化,而且這個(gè)鏈表又是一個(gè)單向鏈表,所以當(dāng)我們使用這個(gè)鏈表的時(shí)候,沒有任何問題。

          而這個(gè)對(duì)象:

          4a64eccedcdd50ce5edb6cdd828a680c.webp

          已經(jīng)沒有任何指針指向它了,那么它不經(jīng)過任何處理,也是可以被 GC 回收掉的。

          對(duì)嗎?

          你細(xì)細(xì)的品一品,是不是這個(gè)道理,從 GC 的角度來說它確實(shí)是“不可達(dá)了”,確實(shí)可以被回收掉了。

          42e6da1f67b0286685b91ad58b6f09ca.webp

          所以,當(dāng)時(shí)有人問了我這樣的一個(gè)問題:

          6bd0a336fcd007c1f374a6f8efefeb9e.webp

          我經(jīng)過上面的一頓分析,發(fā)現(xiàn):嗯,確實(shí)是這樣的,確實(shí)沒啥卵用啊,不寫這一行代碼,功能也是完成正常的。

          但是當(dāng)時(shí)我是這樣回復(fù)的:

          ec3d34a7793cac54e4863337ffbecdc4.webp

          我沒有把話說滿,因?yàn)檫@一行故意寫了一行“help GC”的注釋,可能有 GC 方面的考慮。

          那么到底有沒有 GC 方面的考慮,是怎么考慮的呢?

          憑借著我這幾年寫文章的敏銳嗅覺,我覺得這里“大有文章”,于是我?guī)е@個(gè)問題,在網(wǎng)上溜達(dá)了一圈,還真有收獲。

          help GC?

          首先,一頓搜索,排除了無數(shù)個(gè)無關(guān)的線索之后,我在 openjdk 的 bug 列表里面定位到了這樣的一個(gè)鏈接:

          https://bugs.openjdk.org/browse/JDK-6805775

          f6a47b5dd5479ee82daa8e491e95d5fd.webp

          點(diǎn)擊進(jìn)這個(gè)鏈接的原因是標(biāo)題當(dāng)時(shí)就把吸引到了,翻譯過來就是說:LinkedBlockingQueue 的節(jié)點(diǎn)應(yīng)該在成為“垃圾”之前解除自己的鏈接。

          先不管啥意思吧,反正 LinkedBlockingQueue、Nodes、unlink、garbage 這些關(guān)鍵詞是完全對(duì)上了。

          于是我看了一下描述部分,主要關(guān)心到了這兩個(gè)部分:

          b25c4a25626b26e09bcdcfd240f76f2d.webp

          看到標(biāo)號(hào)為 ① 的地方,我才發(fā)現(xiàn)在 JDK 6 里面對(duì)應(yīng)實(shí)現(xiàn)是這樣的:

          cf1700b799693bdc6353354c68265964.webp

          而且當(dāng)時(shí)的方法還是叫 extract 而不是 dequeue。

          這個(gè)方法名稱的變化,也算是一處小細(xì)節(jié)吧。

          f873103f275dc86ae320e7da0a14f558.webp

          dequeue 是一個(gè)更加專業(yè)的叫法:

          6bc89fbb201c2f0b8a44a308329b577f.webp

          仔細(xì)看 JDK 6 中的 extract 方法,你會(huì)發(fā)現(xiàn),根本就沒有 help GC 這樣的注釋,也沒有相關(guān)的代碼。

          它的實(shí)現(xiàn)方式就是我前面畫圖的這種:

          104a3715018704dc45da30ea367b38c6.webp

          也就是說這行代碼一定是出于某種原因,在后面的 JDK 版本中加上的。那么為什么要進(jìn)行標(biāo)號(hào)為 ① 處那樣的修改呢?

          標(biāo)號(hào)為 ② 的地方給到了一個(gè)鏈接,說是這個(gè)鏈接里面有關(guān)于這個(gè)問題深入的討論。

          For details and in-depth discussion, see:
          http://thread.gmane.org/gmane.comp.java.jsr.166-concurrency/5758

          我非常確信我找對(duì)了地方,而且我要尋找的答案就在這個(gè)鏈接里面。

          但是當(dāng)我點(diǎn)過去的時(shí)候,我發(fā)現(xiàn)不管怎么訪問,這個(gè)鏈接訪問不到了...

          雖然這里的線索斷了,但是順藤摸瓜,我找到了這個(gè) BUG 鏈接:

          https://bugs.openjdk.org/browse/JDK-6806875

          325a47a48ddad86c6f0c793c150be2df.webp

          這兩個(gè) BUG 鏈接說的其實(shí)是同一個(gè)事情,但是這個(gè)鏈接里面給了一個(gè)示例代碼。

          這個(gè)代碼比較長,我給你截個(gè)圖,你先不用細(xì)看,只是對(duì)比我框起來的兩個(gè)部分,你會(huì)發(fā)現(xiàn)這兩部分的代碼其實(shí)是一樣的:

          8d2033b31adcfef12b88f913cebe18b7.webp

          當(dāng) LinkedBlockingQueue 里面加入了 h.next=null 的代碼,跑上面的程序,輸出結(jié)果是這樣:

          d323829cfc74f3807ee0dd93392e796d.webp

          但是,當(dāng) LinkedBlockingQueue 使用 JDK 6 的源碼跑,也就是沒有 h.next=null 的代碼跑上面的程序,輸出結(jié)果是這樣:

          9e148dcf0c41726d598019ce266e8d7b.webp

          產(chǎn)生了 47 次 FGC。

          這個(gè)代碼,在我的電腦上跑,我用的是 JDK 8 的源碼,然后注釋掉 h.next = h 這行代碼,只是會(huì)觸發(fā)一次 FGC,時(shí)間差距是 2 倍:

          378fe1bf2c7e383f69e309aa387a489c.webp

          加上 h.next = h,兩次時(shí)間就相對(duì)穩(wěn)定:

          b0687f4a49dcd7732563471b87f9bf92.webp

          好,到這里,不管原理是什么,我們至少驗(yàn)證了,在這個(gè)地方必須要 help GC 一下,不然確實(shí)會(huì)有性能影響。

          但是,到底是為什么呢?

          4f475d222beb26c4abee14887bbb2c64.webp

          在反復(fù)仔細(xì)的閱讀了這個(gè) BUG 的描述部分之后,我大概懂了。

          最關(guān)鍵的一個(gè)點(diǎn)其實(shí)是藏在了前面示例代碼中我標(biāo)注了五角星的那一行注釋:

          SAME test, but create the queue before GC, head node will be in old gen(頭節(jié)點(diǎn)會(huì)進(jìn)入老年代)

          我大概知道問題的原因是因?yàn)椤癶ead node will be in old gen”,但是具體讓我描述出來我也有點(diǎn)說不出來。

          說人話就是:我懂一點(diǎn),但是不多。

          于是又經(jīng)過一番查找,我找到了這個(gè)鏈接,在這里面徹底搞明白是怎么一回事了:

          http://concurrencyfreaks.blogspot.com/2016/10/self-linking-and-latency-life-of.html

          在這個(gè)鏈接里面提到了一個(gè)視頻,它讓我從第 23 分鐘開始看:

          723136d0728878323a7fe8c8cc4b92b4.webp

          我看了一下這個(gè)視頻,應(yīng)該是 2015 年發(fā)布的。因?yàn)檎麄€(gè)會(huì)議的主題是:20 years of Java, just the beginning:

          https://www.infoq.com/presentations/twitter-services/

          8cebd08eb1d7bc8a503cfa3e1bdf16f7.webp

          這個(gè)視頻的主題是叫做“Life if a twitter JVM engineer”,是一個(gè) twitter 的 JVM 工程師在大會(huì)分享的在工作遇到的一些關(guān)于 JVM 的問題。

          雖然是全程英文,但是你知道的,我的 English level 還是比較 high 的。

          日常聽說,問題不大。所以大概也就聽了個(gè)幾十遍吧,結(jié)合著他的 PPT 也就知道關(guān)于這個(gè)部分他到底在分享啥了。

          我要尋找的答案,也藏在這個(gè)視頻里面。

          我挑關(guān)鍵的給你說。

          首先他展示了這樣的這個(gè)圖片:

          37efae78db640410b2e43749a6a56a01.webp

          老年代的 x 對(duì)象指向了年輕代的 y 對(duì)象。一個(gè)非常簡單的示意圖,他主要是想要表達(dá)“跨代引用”這個(gè)問題。

          然后,出現(xiàn)了這個(gè)圖片:

          f7cbac0328b3ca694c5fc97a08969d3a.webp

          這里的 Queue 就是本文中討論的 LinkedBlockingQueue。

          首先可以看到整個(gè) Queue 在老年代,作為一個(gè)隊(duì)列對(duì)象,極有可能生命周期比較長,所以隊(duì)列在老年代是一個(gè)正常的現(xiàn)象。

          然后我們往這個(gè)隊(duì)列里面插入了 A,B 兩個(gè)元素,由于這兩個(gè)元素是我們剛剛插入的,所以它們?cè)谀贻p代,也沒有任何毛病。

          此時(shí)就出現(xiàn)了老年代的 Queue 對(duì)象,指向了位于年輕代的 A,B 節(jié)點(diǎn),這樣的跨代引用。

          接著,A 節(jié)點(diǎn)被干掉了,出隊(duì):

          ea61c2eb8c66be787323a624062cc82f.webp

          A 出隊(duì)的時(shí)候,由于它是在年輕代的,且沒有任何老年代的對(duì)象指向它,所以它是可以被 GC 回收掉的。

          同理,我們插入 D,E 節(jié)點(diǎn),并讓 B 節(jié)點(diǎn)出隊(duì):

          bd83feb326703f4ca41bae3923722c3a.webp

          假設(shè)此時(shí)發(fā)生一次 YGC, A,B 節(jié)點(diǎn)由于“不可達(dá)”被干掉了,C 節(jié)點(diǎn)在經(jīng)歷幾次 YGC 之后,由于不是“垃圾”,所以晉升到了老年代:

          5e5abec58cdf13c8c98aba7b594b9a12.webp

          這個(gè)時(shí)候假設(shè) C 出隊(duì),你說會(huì)出現(xiàn)什么情況?

          fde7b2660809b95bb75317a5a371b750.webp

          首先,我問你:這個(gè)時(shí)候 C 出隊(duì)之后,它是否是垃圾?

          肯定是的,因?yàn)樗豢蛇_(dá)了嘛。從圖片上也可以看到,C 雖然在老年代,但是沒有任何對(duì)象指向它了,它確實(shí)完?duì)僮恿耍?/p>

          d09fe4cf64d289906a5aae92151c643f.webp

          好,接下來,請(qǐng)坐好,認(rèn)真聽了。

          此時(shí),我們加入一個(gè) F 節(jié)點(diǎn),沒有任何毛?。?/p>

          d79ca3ecdfdd29a4ee9cad58a607cf3f.webp

          接著 D 元素被出隊(duì)了:

          bfc0435d0692f3f47ab248c7202c7761.webp

          就像下面這個(gè)動(dòng)圖一樣:

          a472389d6b2f721f3a3b22d7b5ed4621.webp

          我把這一幀拿出來,針對(duì)這個(gè) D 節(jié)點(diǎn),單獨(dú)的說:

          a472389d6b2f721f3a3b22d7b5ed4621.webp

          假設(shè)在這個(gè)時(shí)候,再次發(fā)生 YGC,D 節(jié)點(diǎn)雖然出隊(duì)了,它也位于年輕代。但是位于老年代的 C 節(jié)點(diǎn)還指向它,所以在 YGC 的時(shí)候,垃圾回收線程不敢動(dòng)它。

          因此,在幾輪 YGC 之后,本來是“垃圾”的 D,搖身一變,進(jìn)入老年代了:

          3973246bd1446a6ba4cd0fec010bc996.webp

          雖然它依然是“垃圾”,但是它進(jìn)入了老年代,YGC 對(duì)它束手無策,得 FGC 才能干掉它了。

          然后越來越多的出隊(duì)節(jié)點(diǎn),變成了這樣:

          7d11dfc81da7d3a26e910f2e1a8b2900.webp

          然后,他們都進(jìn)入了老年代:

          fae68d3377e84854d46bc2629957ff3d.webp

          我們站在上帝視角,我們知道,這一串節(jié)點(diǎn),應(yīng)該在 YGC 的時(shí)候就被回收掉。

          但是這種情況,你讓 GC 怎么處理?

          它根本就處理不了。

          GC 線程沒有上帝視角,站在它的視角,它做的每一步動(dòng)作都是正確的、符合規(guī)定的。最終呈現(xiàn)的效果就是必須要經(jīng)歷 FGC 才能把這些本來早就應(yīng)該回收的節(jié)點(diǎn),進(jìn)行回收。而我們知道,F(xiàn)GC 是應(yīng)該盡量避免的,所以這個(gè)處置方案,還是“差點(diǎn)意思”的。

          所以,我們應(yīng)該怎么辦?

          你回想一下,萬惡之源,是不是這個(gè)時(shí)候:

          d09fe4cf64d289906a5aae92151c643f.webp

          C 雖然被移出隊(duì)列了,但是它還持有一個(gè)下一個(gè)節(jié)點(diǎn)的引用,讓這個(gè)引用變成跨代引用的時(shí)候,就出毛病了。

          所以,help GC,這不就來了嗎?

          70be0f009bab7b4ce42cbe01c4bdfd4d.webp

          不管你是位于年輕代還是老年代,只要是出隊(duì),就把你的 next 引用干掉,杜絕出現(xiàn)前面我們分析的這種情況。

          這個(gè)時(shí)候,你再回過頭去看前面提到的這句話:

          head node will be in old gen...

          你就應(yīng)該懂得起,為什么 head node 在 old gen 就要出事兒。

          h.next=null ???

          前面一節(jié),經(jīng)過一頓分析之后,知道了為什么要有這一行代碼:

          01fc7775ea34b624bbdb8c33257e655d.webp

          但是你仔細(xì)一看,在我們的源碼里面是 h.hext=h 呀?

          而且,經(jīng)過前面的分析我們可以知道,理論上,h.next=null 和 h.hext=h 都能達(dá)到 help GC 的目的,那么為什么最終的寫法是 h.hext=h 呢?

          或者換句話說:為什么是 h.next=h,而不是 h.next=null 呢?

          針對(duì)這個(gè)問題,我也盯著源碼,仔細(xì)思考了很久,最終得出了一個(gè)“非常大膽”的結(jié)論是:這兩個(gè)寫法是一樣的,不過是編碼習(xí)慣不一樣而已。

          但是,注意,我要說但是了。

          再次經(jīng)過一番查詢、分析和論證,這個(gè)地方它還必須得是 h.next=h。

          因?yàn)樵谶@個(gè) bug 下面有這樣的一句討論:

          b20af625c0df45ecf15c003ff2f51b53.webp

          關(guān)鍵詞是:weakly consistent iterator,弱一致性迭代器。也就是說這個(gè)問題的答案是藏在 iterator 迭代器里面的。

          在 iterator 對(duì)應(yīng)的源碼中,有這樣的一個(gè)方法:

          java.util.concurrent.LinkedBlockingQueue.Itr#nextNode

          3a8cbf5f5ddf2464f03dd849436780c3.webp

          針對(duì) if 判斷中的 s==p,我們把 s 替換一下,就變成了 p.next=p:

          562f2e5796551c2bb4c138a30e5d4a1c.webp

          那么什么時(shí)候會(huì)出現(xiàn) p.next=p 這樣的代碼呢?

          答案就藏在這個(gè)方法的注釋部分:dequeued nodes (p.next == p)

          1432c38ffee32e62f5f3bddbf15104a5.webp

          dequeue 這不是巧了嗎,這不是和前面給呼應(yīng)起來了嗎?

          好,到這里,我要開始給你畫圖說明了,假設(shè)我們 LinkedBlockingQueue 里面放的元素是這樣的:

          675f22db9c98e9c3e2600f1f6173613c.webp

          畫圖出來就是這樣的:

          16ae21e59aa07b361bb8283ba60e5926.webp

          現(xiàn)在我們要對(duì)這個(gè)鏈表進(jìn)行迭代,對(duì)應(yīng)到畫圖就是這樣的:

          linkedBlockingQueue.iterator();

          649e4a1184e2430e1f74ec425b63b7cd.webp

          看到這個(gè)圖的時(shí)候,問題就來了:current 指針是什么時(shí)候冒出來的呢?

          current,這個(gè)變量是在生成迭代器的時(shí)候就初始化好了的,指向的是 head.next:

          4462030aea48ee66c8cba5266d9cf271.webp

          然后 current 是通過 nextNode 這個(gè)方法進(jìn)行維護(hù)的:

          ad4a20cc8a5ac0a268d5f3d82c037619.webp

          正常迭代下,每調(diào)用一次都會(huì)返回 s,而 s 又是 p.next,即下一個(gè)節(jié)點(diǎn):

          87e42fa28df54e5a064df434606cec07.webp

          所以,每次調(diào)用之后 current 都會(huì)移動(dòng)一格:

          9be1d6f362a7b70240c006c719b9f884.webp

          這種情況,完全就沒有這個(gè)分支的事兒:

          f825595a49fd4affffd4be10bc6ffc9e.webp

          什么時(shí)候才會(huì)和它扯上關(guān)系呢?

          你想象一個(gè)場(chǎng)景。

          A 線程剛剛要對(duì)這個(gè)隊(duì)列進(jìn)行迭代,而 B 線程同時(shí)在對(duì)這個(gè)隊(duì)列進(jìn)行 remove。

          對(duì)于 A 線程,剛剛開始迭代,畫圖是這樣的:

          8f67e81af2b594aa5bdd2bc1b533a50e.webp

          然后 current 還沒開始移動(dòng)呢,B 線程“咔咔”幾下,直接就把 1,2,3 全部給干出隊(duì)列了,于是站在 B 線程的視角,隊(duì)列是這樣的了:

          68c184cc3b49dcc1d6c5c48c3e2e865f.webp

          到這里,你先思考一個(gè)問題:1,2,3 這幾個(gè)節(jié)點(diǎn),不管是自己指向自己,還是指向一個(gè) null,此時(shí)發(fā)生一個(gè) YGC 它們還在不在?

          2 和 3 指定是沒了,但是 1 可不能被回收了啊。

          因?yàn)殡m然元素為 1 的節(jié)點(diǎn)出隊(duì)了,但是站在 A 線程的視角,它還持有一個(gè) current 引用呢,它還是“可達(dá)”的。

          所以,這個(gè)時(shí)候 A 線程開始迭代,雖然 1 被 B 出隊(duì)了,但是它一樣會(huì)被輸出。

          然后,我們?cè)賮韺?duì)于下面這兩種情況,A 線程會(huì)如何進(jìn)行迭代:

          b09be58834a9d89fd4c2fb67e0cdaca7.webp

          當(dāng) 1 節(jié)點(diǎn)的 next 指為 null 的時(shí)候,即 p.next 為 null,那么滿足 s==null 的判斷,所以 nextNode 方法就會(huì)返回 s,也就是返回了 null:

          4e8e5c5b001e1f7e6c42e7b467aa1ea7.webp

          當(dāng)你調(diào)用 hasNext 方法判斷是否還有下一節(jié)點(diǎn)的時(shí)候,就會(huì)返回 false,循環(huán)就結(jié)束了:

          fe3ef09bf1010204d4984a0d6b5bc787.webp

          然后,我們站在上帝視角是知道的,后面還有 4 和 5 沒輸出呢,所以這樣就會(huì)出現(xiàn)問題。

          但是,當(dāng) 1 節(jié)點(diǎn)的 next 指向自己的時(shí)候,有趣的事情就來了:

          ee39fbdbf93273b462af4ba4a4b9e065.webp

          current 指針就變成了 head.next。

          而你看看當(dāng)前的這個(gè)鏈表里面 head.next 是啥?

          977a9a6e03c7c92d4dafe71bd9963a7a.webp

          不就是 4 節(jié)點(diǎn)嗎?

          這不就銜接上了嗎?

          所以最終 A 線程會(huì)輸出 1,4,5。

          雖然我們知道 1 元素其實(shí)已經(jīng)出隊(duì)了,但是 A 線程開始迭代的時(shí)候,它至少還在。

          這玩意就體現(xiàn)了前面提到的:weakly consistent iterator,弱一致性迭代器。

          這個(gè)時(shí)候,你再結(jié)合者迭代器上的注解去看,就能搞得明明白白了:

          08aa7afe6bdfd65f83b1bffd288f0144.webp

          如果 hasNext 方法返回為 true,那么就必須要有下一個(gè)節(jié)點(diǎn)。即使這個(gè)節(jié)點(diǎn)被比如 take 等等的方法給移除了,也需要返回它。這就是 weakly-consistent iterator。

          然后,你再看看整個(gè)類開始部分的 Java doc,其實(shí)我整篇文章就是對(duì)于這一段描述的翻譯和擴(kuò)充:

          3f690ae50d8e58699802d4edce05e1b5.webp

          看完并理解我這篇文章之后,你再去看這部分的 Java doc,你就知道它是在說個(gè)啥事情,以及它為什么要這樣的去做這件事情了。

          好了,看到這里,你現(xiàn)在應(yīng)該明白了,為什么必須要有 h.next=h,為什么不能是 h.next=null 了吧?

          明白了就好。

          因?yàn)楸疚木偷竭@里就要結(jié)束了。

          如果你還沒明白,不要懷疑自己,大膽的說出來:什么玩意?寫的彎彎繞繞的,看求不懂。呸,垃圾作者。

          605c724b7b2fec19cdaeaf802750ccda.webp

          最后,我還想要說的是,關(guān)于 LBQ 這個(gè)隊(duì)列,我之前也寫過這篇文章專門說它:《喜提JDK的BUG一枚!多線程的情況下請(qǐng)謹(jǐn)慎使用這個(gè)類的stream遍歷?!?/a>

          文章里面也提到了 dequeue 這個(gè)方法:

          d05137780f64ca42a1d16a78ac57e868.webp

          但是當(dāng)時(shí)我完全沒有思考到文本提到的問題,順著代碼就捋過去了。

          我覺得看到這部分代碼,然后能提出本文中這兩個(gè)問題的人,才是在帶著自己思考深度閱讀源碼的人。

          解決問題不厲害,提出問題才是最屌的,因?yàn)楫?dāng)一個(gè)問題提出來的時(shí)候,它就已經(jīng)被解決了。

          帶著質(zhì)疑的眼光看代碼,帶著求真的態(tài)度去探索,與君共勉之。

          97a58b20cb58c8d4219e026da41bf121.webp

          · · · · · · · · · · · · · · ? ? E N D ? ? · · · · · · · · · · · · · ·

          4d93f393ac03eaed5d3d92c61ba6370c.webp

          推薦?? 記錄一次非常麻煩又磨人的調(diào)試...

          ?? @ E v e n t L i s t e n e r 個(gè) 。

          ?? , 會(huì) 。

          ?? 聯(lián) 網(wǎng) 。

          ?? ? 個(gè) 發(fā) 。

          瀏覽 29
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  二三区视频 | 精品日韩一区二区三区 | A片操逼 爱草在线 | 成人91AV视频 | 婷婷五月天影院 |