<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>

          讀者詭異問(wèn)題,讓我發(fā)現(xiàn)IDEA的Bug

          共 3737字,需瀏覽 8分鐘

           ·

          2021-11-09 12:31

          ????關(guān)注后回復(fù) “進(jìn)群” ,拉你進(jìn)程序員交流群????


          作者丨安琪拉

          來(lái)源丨安琪拉的博客


          讀者的一個(gè)問(wèn)題讓我發(fā)現(xiàn)了IDEA的一個(gè)bug。

          前天下午有讀者在國(guó)服并發(fā)群?jiǎn)柫藗€(gè)問(wèn)題:


          讀者問(wèn)題

          他們看完我這篇文章估計(jì)會(huì)覺(jué)得哦,好吧拉哥原來(lái)還是Google來(lái)的,原來(lái)這么簡(jiǎn)單,哈哈哈,~~~

          我來(lái)大致說(shuō)下這位讀者所說(shuō)的問(wèn)題:

          他寫(xiě)了這么一段代碼:

          public class ConcurrentLinkedQueueTest {

              public static void main(String[] args) {

                  ConcurrentLinkedQueue concurrentLinkedQueue = new ConcurrentLinkedQueue();
                  concurrentLinkedQueue.offer(1);
              }
          }

          然后debug 打算看 ConcurrentLinkedQueue offer函數(shù)的源碼,但是發(fā)生了非常蹊蹺甚至算詭異的事情。

          我這里先大致交代一下 ConcurrentLinkedQueue 是做什么的,ConcurrentLinkedQueue 是并發(fā)非阻塞隊(duì)列,并發(fā)編程場(chǎng)景里面,除了使用阻塞隊(duì)列,類(lèi)似BlockingQueue以外,還有非阻塞隊(duì)列,ConcurrentLinkedQueue 就屬于非阻塞隊(duì)列。

          阻塞隊(duì)列使用鎖來(lái)做線程控制,非阻塞隊(duì)列采用CAS + 循環(huán)來(lái)做并發(fā)控制。

          ConcurrentLinkedQueue 隊(duì)列有head、tail 節(jié)點(diǎn)(頭、尾),尾節(jié)點(diǎn)不一定是最后一個(gè)插入的節(jié)點(diǎn),這里可能會(huì)有點(diǎn)難理解,Doug Lea在這里有些巧妙的設(shè)計(jì)。

          ConcurrentLinkedQueue 實(shí)現(xiàn)機(jī)制不是今天的重點(diǎn),后面具體講并發(fā)編程系列的時(shí)候會(huì)講到,今天只要知道上面交代的背景,我們繼續(xù)。

          第一步:new ConcurrentLinkedQueue() 會(huì)初始化頭和尾節(jié)點(diǎn)。


          可以看到初始化完成之后,head、tail都指向 $Node@524, 你可以姑且把它當(dāng)做內(nèi)存地址;

          第二步,ConcurrentLinkedQueue.offer(1); 向隊(duì)列中插入一個(gè)元素

          代碼如下圖,插入元素都會(huì)初始化一個(gè)新Node節(jié)點(diǎn)來(lái)存放要插入的值,初始化的Node值指向 $Node@528;


          到這里都沒(méi)什么問(wèn)題,我們繼續(xù)往下走。

          一直到 p.casNext, 大家看下面,p.casNext 是通過(guò)cas 將p 節(jié)點(diǎn)的next節(jié)點(diǎn)設(shè)置為新插入的newNode 節(jié)點(diǎn),但是見(jiàn)證奇跡的時(shí)候到了。繼續(xù)放下看。

          image-20211103222109757

          正常casNext 之后應(yīng)該如下圖所示:

          節(jié)點(diǎn)圖

          但是實(shí)際我們看下debug 模式下head、tail 的數(shù)據(jù)。

          安琪拉
          安琪拉

          head 指向新節(jié)點(diǎn),tail 沒(méi)變,但是tail.next 指向自己。這什么情況?我當(dāng)時(shí)第一反應(yīng)Doug Lea 搞了什么黑科技?!!!

          但是實(shí)在不敢相信,看了這么多源碼還看不懂這么一小段代碼,我潛意識(shí)覺(jué)得肯定有什么地方不對(duì)。于是Google 搜了一下,在全球最大同性交友網(wǎng)站Stack Overflow找到了問(wèn)題,但是沒(méi)有人回答原因。

          問(wèn)題:https://stackoverflow.com/questions/55889152/why-my-object-has-been-changed-by-intellij-ideas-debugger-soundlessly

          產(chǎn)品經(jīng)理解釋

          看了這個(gè)回答,趕緊試了一下,問(wèn)題解決,配置就是把 IDEA的二項(xiàng)配置去掉就好了。

          配置截圖

          但是不爽,去IDEA 看了二項(xiàng)配置的解釋?zhuān)?/p>

          官網(wǎng)截圖

          Enable alternative view for Collections classes 是IDEA 會(huì)為集合創(chuàng)建開(kāi)啟一個(gè)視圖。

          Enable toString 會(huì)為所有對(duì)象開(kāi)啟toString方法,建議關(guān)閉,因?yàn)槿绻写髮?duì)象,debug 模式toString 非常耗性能,而且debug模式 toString 是全局的,有時(shí)候debug 模式卡的你懷疑人生。

          繼續(xù)說(shuō)下為什么會(huì)出現(xiàn) head 節(jié)點(diǎn)被篡改的原因,都指向新插入的newNode節(jié)點(diǎn)上去了。原因是debug模式開(kāi)啟了toString 和 集合視圖,這二個(gè)屬性開(kāi)啟都會(huì)導(dǎo)致IDEA 為 ConcurrentLinkedQueue 生成迭代器,遍歷集合,遍歷的時(shí)候調(diào)用 first() 函數(shù),在ConcurrentLinkedQueue first() 中 head節(jié)點(diǎn)被更新了。

          注釋也很清楚,first 是將head 節(jié)點(diǎn)更新為第一個(gè)item值不是null 的節(jié)點(diǎn),如果隊(duì)列只有一個(gè)節(jié)點(diǎn)(除了頭結(jié)點(diǎn)),就設(shè)置為這個(gè)頭結(jié)點(diǎn),上面那個(gè)bug(為什么head指向newNode節(jié)點(diǎn))就都解釋清楚了。

          上面offer 那個(gè) for 循環(huán)那段代碼我簡(jiǎn)單解釋一下,就是找到最后一個(gè)節(jié)點(diǎn),然后將newNode插入到最后一個(gè)節(jié)點(diǎn)的后面,如果tail 節(jié)點(diǎn)不是最后一個(gè)節(jié)點(diǎn),這時(shí)候在最后一個(gè)節(jié)點(diǎn)成功插入元素了,這時(shí)候CAS 更新tail為新插入的節(jié)點(diǎn)( casTail(t, newNode) ) 。還記得我前面說(shuō)過(guò)的tail 節(jié)點(diǎn)不一定指向最后一個(gè)節(jié)點(diǎn),所以要找最后一個(gè)節(jié)點(diǎn)。

          ConcurrentLinkedQueue 后面講并發(fā)編程的時(shí)候還會(huì)再詳細(xì)介紹,今天的IDEA bug分析先到這里。

          同時(shí)在分析ConcurrentLinkedQueue的時(shí)候,我還發(fā)現(xiàn) 《Java并發(fā)編程的藝術(shù)》這本書(shū)的作者方騰飛的一個(gè)錯(cuò)誤,如下:

          他說(shuō)的:

          只有一種可能p節(jié)點(diǎn)和p的next節(jié)點(diǎn)都為空,表示這個(gè)隊(duì)列剛初始化,正準(zhǔn)備添加第一個(gè)節(jié)點(diǎn)

          實(shí)際上是錯(cuò)誤的,是因?yàn)閜oll 移除元素的時(shí)候會(huì)出現(xiàn)p.next = p 自引用的情況,而不是初始化,初始化 p = null,p

          .next = p 肯定直接NPE了。


          最后上面這張圖上還有個(gè) “Hide null elements in arrays and collections” 這個(gè)建議取消掉。

          不取消會(huì)有什么效果:

          image-20211103232458655

          null 元素都沒(méi)展示出來(lái)。有時(shí)候debug 容易忽視null 的存在,導(dǎo)致出現(xiàn)問(wèn)題,雖然有一行提示:Not showing null elements。

          關(guān)閉后如下圖。


          我是安琪拉,今天分享先到這里。

          -End-

          最近有一些小伙伴,讓我?guī)兔φ乙恍?nbsp;面試題 資料,于是我翻遍了收藏的 5T 資料后,匯總整理出來(lái),可以說(shuō)是程序員面試必備!所有資料都整理到網(wǎng)盤(pán)了,歡迎下載!

          點(diǎn)擊??卡片,關(guān)注后回復(fù)【面試題】即可獲取

          在看點(diǎn)這里好文分享給更多人↓↓

          瀏覽 24
          點(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>
                  柏欣彤Av色福利一二区 | 中国女人真人一级毛片 | a 在线天堂| 无码15P | 伊人久操视频 |