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

          面試官問我平時(shí)怎么看源碼的,我把這篇文章甩給他了。

          共 7366字,需瀏覽 15分鐘

           ·

          2020-12-16 21:52

          本文來自作者投稿,原作者:WwpwW

          1.1,為什么要分析源碼?

          分析源碼可以培養(yǎng)一下自己獨(dú)立思考問題的能力(愿意讀源碼找問題的能力),最重要的是我們不用再買紙質(zhì)書去學(xué)習(xí)數(shù)據(jù)結(jié)構(gòu)了,數(shù)據(jù)結(jié)構(gòu)的應(yīng)用都在源碼里面了,正如那句被人熟知的"營(yíng)養(yǎng)都在湯里面"一樣,當(dāng)我們看過一遍一遍數(shù)據(jù)結(jié)構(gòu)的理論知識(shí)后還是想不起它在哪里用到時(shí),可能看一看源碼加上自己的一點(diǎn)思考時(shí)就知道它的使用場(chǎng)景了,解答完畢。

          1.2,分析源碼的好處是?

          其實(shí),對(duì)于工作一段時(shí)間的小伙伴來說,我們都是面向業(yè)務(wù)開發(fā),就是被人飯后談資的增刪改查程序員/程序猿,當(dāng)然了,有的人說起來這樣的話,"api調(diào)包俠"就有點(diǎn)過分了,其實(shí)對(duì)于我個(gè)人來說,我是受不了這樣的話語的,因?yàn)樵鰟h改查是常用的操作,即滿足了"二八原則",其實(shí),程序員/程序猿都是工作中不可或缺的一部分,也是企業(yè)開發(fā)應(yīng)用中重要的一環(huán),分析源碼可能就會(huì)帶來顯而易見的內(nèi)功提升,其次就是分析源碼的過程中就是在向優(yōu)秀的人學(xué)習(xí)的一種體現(xiàn),畢竟源碼里面隱藏了大師多年的心得和思想。

          1.3,如何分析源碼?

          在整個(gè)文章的閱讀過程中,想必你已經(jīng)學(xué)會(huì)了如何分析源碼了,從哪入手,這也是一種潛移默化的過程。

          本文以分析LinkedBlockingQueue的源碼為例,介紹下應(yīng)該如何看源碼!

          二, ?方法分析

          2.1,構(gòu)造函數(shù)

          public?LinkedBlockingQueue()?{
          ?//創(chuàng)建容量為整形最大值
          ????this(Integer.MAX_VALUE);
          }

          public?LinkedBlockingQueue(int?capacity)?{
          ????//若capacity小于0,則直接拋出參數(shù)不合法的異常
          ????if?(capacity?<=?0)?throw?new?IllegalArgumentException();
          ????this.capacity?=?capacity;
          ????//默認(rèn)創(chuàng)建一個(gè)元素為null的節(jié)點(diǎn)Node
          ????last?=?head?=?new?Node(null);
          }

          2.2,add()方法

          public?boolean?add(E?e)?{
          ????//添加到隊(duì)列的末尾
          ????????addLast(e);
          ????????return?true;
          ????}
          //第二步
          public?void?addLast(E?e)?{
          ????//若添加失敗,則直接拋出隊(duì)列已滿的異常信息,給與提示
          ????????if?(!offerLast(e))
          ????????????throw?new?IllegalStateException("Deque?full");
          ????}
          //第三步
          public?boolean?offerLast(E?e)?{
          ????//若添加的元素e為null,則直接拋出空指針異常,因?yàn)椴辉试S添加元素為null的情況
          ????????if?(e?==?null)?throw?new?NullPointerException();
          ????//構(gòu)造一個(gè)元素為e的節(jié)點(diǎn)node
          ????????Node?node?=?new?Node(e);
          ????????final?ReentrantLock?lock?=?this.lock;
          ????//進(jìn)行加鎖操作
          ????????lock.lock();
          ????????try?{
          ????????????//進(jìn)行第四步操作
          ????????????return?linkLast(node);
          ????????}?finally?{
          ?????//進(jìn)行釋放鎖,當(dāng)然了,這里你要記住鎖釋放是放到finally語句塊里面的(重要)
          ????????????lock.unlock();
          ????????}
          ????}
          //第四步
          private?boolean?linkLast(Node?node)?{
          ????????//?assert?lock.isHeldByCurrentThread();
          ????//如果隊(duì)列里元素個(gè)數(shù)大于等于了隊(duì)列的容量,說明此時(shí)不能再將元素放入隊(duì)列里面了,直接返回false即可
          ????????if?(count?>=?capacity)
          ????????????return?false;
          ????//創(chuàng)建一個(gè)臨時(shí)變量l,將最后一個(gè)節(jié)點(diǎn)的引用賦值給l
          ????????Node?l?=?last;
          ????//將最后一個(gè)節(jié)點(diǎn)的引用賦值給新節(jié)點(diǎn)node的前一個(gè)引用(鏈表的特點(diǎn))
          ????????node.prev?=?l;
          ????//將新節(jié)點(diǎn)node賦值給最后一個(gè)節(jié)點(diǎn)(因?yàn)樵厝珀?duì)列是放在隊(duì)列的末尾的,隊(duì)列的特點(diǎn)->先進(jìn)先出)
          ????????last?=?node;
          ????//為什么這里要判斷first是否為null呢?因?yàn)樘砑訒r(shí)不知道隊(duì)列里是否已經(jīng)存在元素,若first為null,說明隊(duì)列里沒有元素
          ????????if?(first?==?null)
          ????//此時(shí)的node就是第一個(gè)結(jié)點(diǎn),賦值即可
          ????????????first?=?node;
          ????????else
          ????//將新節(jié)點(diǎn)node掛在原有結(jié)點(diǎn)的下一個(gè),即l.next=node
          ????????????l.next?=?node;
          ????//隊(duì)列元素個(gè)數(shù)進(jìn)行加一
          ????????++count;
          ????//發(fā)送一個(gè)信號(hào),隊(duì)列不滿的信號(hào)
          ????????notEmpty.signal();
          ????//默認(rèn)將元素e添加到隊(duì)列里面了~,即返回true
          ????????return?true;
          ????}

          2.3,size()方法

          ?public?int?size()?{
          ?????//直接返回隊(duì)列里表示元素個(gè)數(shù)的成員變量count即可
          ????????return?count.get();
          ????}

          2.4,peek()方法

          public?E?peek()?{
          ????//若隊(duì)列元素個(gè)數(shù)為0,說明隊(duì)列里還未有元素,直接返回null
          ????????if?(count.get()?==?0)
          ????????????return?null;
          ????//獲取鎖
          ????????final?ReentrantLock?takeLock?=?this.takeLock;
          ????//進(jìn)行加鎖操作
          ????????takeLock.lock();
          ????????try?{
          ????//獲取隊(duì)列的第一個(gè)結(jié)點(diǎn)引用first
          ????????????Node?first?=?head.next;
          ????//若隊(duì)列的第一個(gè)節(jié)點(diǎn)引用為null,則直接返回null即可
          ????????????if?(first?==?null)
          ????????????????return?null;
          ????????????else
          ?????//獲取第一個(gè)節(jié)點(diǎn)引用first的元素item返回
          ????????????????return?first.item;
          ????????}?finally?{
          ????//進(jìn)行釋放鎖,記得釋放鎖要放在finally語句塊里,確保鎖可以得到釋放
          ????????????takeLock.unlock();
          ????????}
          ????}

          2.5,contains()方法

          public?boolean?contains(Object?o)?{
          ????//引入隊(duì)列里不允許放入null,所以若元素o為null,則直接返回false,相當(dāng)于進(jìn)行了前置校驗(yàn)操作
          ????????if?(o?==?null)?return?false;
          ????//第二步
          ????????fullyLock();
          ????????try?{
          ????//循環(huán)遍歷隊(duì)列的每個(gè)節(jié)點(diǎn)node的元素item是否與元素o相等,若存在相等元素,則包含,返回true即可
          ????????????for?(Node?p?=?head.next;?p?!=?null;?p?=?p.next)
          ????????????????if?(o.equals(p.item))
          ????????????????????return?true;
          ????????????return?false;
          ????????}?finally?{
          ?????//第四步,第三次說明了,釋放鎖要放在finally語句塊里面,確保鎖可以得到正確的釋放
          ????????????fullyUnlock();
          ????????}
          ????}
          ????/**
          ?????*?Locks?to?prevent?both?puts?and?takes.
          ?????*/
          ???//第二步,上面的注釋說明,進(jìn)行加鎖操作,禁止進(jìn)行添加元素到隊(duì)列,禁止進(jìn)行從隊(duì)列里取元素,下面就慢慢分析take()方法了
          ????void?fullyLock()?{
          ????????putLock.lock();
          ????????takeLock.lock();
          ????}
          ???//第四步,因?yàn)榧渔i和釋放鎖是成對(duì)的,所以最后一定要記得釋放鎖哈~,即加了什么鎖,要對(duì)應(yīng)的解鎖
          ???/**
          ?????*?Unlocks?to?allow?both?puts?and?takes.
          ?????*/
          ????void?fullyUnlock()?{
          ????????takeLock.unlock();
          ????????putLock.unlock();
          ????}

          2.6,put()方法

          public?void?put(E?e)?throws?InterruptedException?{
          ????//這個(gè)隊(duì)列是不允許添加空元素的,先來個(gè)前置校驗(yàn),元素為null,則拋出NPE異常
          ????????if?(e?==?null)?throw?new?NullPointerException();
          ????????//?Note:?convention?in?all?put/take/etc?is?to?preset?local?var
          ????????//?holding?count?negative?to?indicate?failure?unless?set.
          ????????int?c?=?-1;
          ????//構(gòu)造一個(gè)節(jié)點(diǎn)node,元素為e
          ????????Node?node?=?new?Node(e);
          ????//獲取putLock這把鎖引用
          ????????final?ReentrantLock?putLock?=?this.putLock;
          ????????final?AtomicInteger?count?=?this.count;
          ????????putLock.lockInterruptibly();
          ????????try?{
          ?????//當(dāng)隊(duì)列元素個(gè)數(shù)等于隊(duì)列容量capacity時(shí),進(jìn)行等待,這里存在一個(gè)阻塞操作
          ????????????while?(count.get()?==?capacity)?{
          ????????????????notFull.await();
          ????????????}
          ??????//第二步操作,入隊(duì)列
          ????????????enqueue(node);
          ??????//元素個(gè)數(shù)增加1,這里使用的是cas機(jī)制
          ????????????c?=?count.getAndIncrement();
          ????????????if?(c?+?1???????//進(jìn)行信號(hào)的通知,和notify一樣
          ????????????????notFull.signal();
          ????????}?finally?{
          ??????//釋放鎖操作
          ????????????putLock.unlock();
          ????????}
          ????????if?(c?==?0)
          ????????????signalNotEmpty();
          ????}
          ???//第二步,入隊(duì)列操作(隊(duì)列的特點(diǎn)是先進(jìn)先出)
          ????private?void?enqueue(Node?node)?{
          ????????//將新元素結(jié)點(diǎn)node掛在原有隊(duì)列最后一個(gè)元素的后面,然后將最后一個(gè)節(jié)點(diǎn)的引用賦值給last
          ????????last?=?last.next?=?node;
          ????}

          2.7,take()方法

          public?E?take()?throws?InterruptedException?{
          ????????E?x;
          ????????int?c?=?-1;
          ????????final?AtomicInteger?count?=?this.count;
          ?????//獲取takeLock鎖引用
          ????????final?ReentrantLock?takeLock?=?this.takeLock;
          ?????//這把鎖是可以中斷的
          ????????takeLock.lockInterruptibly();
          ????????try?{
          ?????//若隊(duì)列元素個(gè)數(shù)為0,說明隊(duì)列里沒元素了,此時(shí)需要進(jìn)行發(fā)送一個(gè)等待的通知
          ????????????while?(count.get()?==?0)?{
          ????????????????notEmpty.await();
          ????????????}
          ?????//進(jìn)行從隊(duì)列里進(jìn)行取元素操作,見下面的第二步操作?
          ????????????x?=?dequeue();
          ?????//隊(duì)列元素個(gè)數(shù)進(jìn)行減一操作
          ????????????c?=?count.getAndDecrement();
          ????????????if?(c?>?1)
          ????????????????notEmpty.signal();
          ????????}?finally?{
          ??????//?釋放鎖
          ????????????takeLock.unlock();
          ????????}
          ????????if?(c?==?capacity)
          ????????????signalNotFull();
          ????????return?x;
          ????}
          ???//第二步操作
          ????private?E?dequeue()?{
          ???//下面的操作就是隊(duì)首元素出來了,隊(duì)列的后面元素要前移,如果這一步不是很好理解的話,可以按照下面的方式進(jìn)行debug看下
          ???//在分析這塊時(shí),自己也有所成長(zhǎng),因?yàn)閐ebug是可以看到元素在數(shù)據(jù)流中如何處理的
          ????????Node?h?=?head;
          ????????Node?first?=?h.next;
          ????????h.next?=?h;?//?help?GC
          ????????head?=?first;
          ???//獲取隊(duì)首元素x
          ????????E?x?=?first.item;
          ????//觸發(fā)gc機(jī)制進(jìn)行垃圾回收,什么是垃圾對(duì)象呢,就是不可達(dá)對(duì)象,不了解的可以看下jvm對(duì)應(yīng)的機(jī)制
          ????????first.item?=?null;
          ????//返回隊(duì)列的隊(duì)首元素
          ????????return?x;
          ????}
          image-20201122102406286

          2.8,remove()方法

          ?public?boolean?remove(Object?o)?{
          ?????//這個(gè)隊(duì)列里不允許添加元素為null的元素,所以這里在刪除的時(shí)候做了一下前置校驗(yàn)
          ????????if?(o?==?null)?return?false;
          ?????//第二步,禁止入隊(duì)列和出隊(duì)列操作,和上文的contains()方法采取的策略一致
          ????????fullyLock();
          ????????try?{
          ??????//循環(huán)遍歷隊(duì)列的每個(gè)元素,進(jìn)行比較,這里是不是和你寫業(yè)務(wù)邏輯方法一樣,嘖嘖,有點(diǎn)意思吧
          ??????//這里你就明白為什么要看源碼了,以及看源碼你能得到什么
          ????????????for?(Node?trail?=?head,?p?=?trail.next;
          ?????????????????p?!=?null;
          ?????????????????trail?=?p,?p?=?p.next)?{
          ???????//此時(shí)找到待刪除的元素o
          ????????????????if?(o.equals(p.item))?{
          ???????//進(jìn)行第三步操作
          ????????????????????unlink(p,?trail);
          ????????????????????return?true;
          ????????????????}
          ????????????}
          ????????????return?false;
          ????????}?finally?{
          ????????????fullyUnlock();
          ????????}
          ????}
          //第二步,禁止入隊(duì)列,出隊(duì)列操作
          ?void?fullyLock()?{
          ????????putLock.lock();
          ????????takeLock.lock();
          ????}
          //第三步
          void?unlink(Node?p,?Node?trail)?{
          ???//觸發(fā)gc機(jī)制,將垃圾對(duì)象進(jìn)行回收
          ????????p.item?=?null;
          ????//將待刪除元素的下一個(gè)節(jié)點(diǎn)【掛載】待刪除元素的前一個(gè)元素的next后面
          ????????trail.next?=?p.next;
          ????//判斷待刪除的元素是否是隊(duì)列的最后一個(gè)元素,如果是,則trail賦值給last,這里你可以想象一下鏈表的刪除操作
          ????????if?(last?==?p)
          ????????????last?=?trail;
          ????//隊(duì)列的元素個(gè)數(shù)減一
          ????????if?(count.getAndDecrement()?==?capacity)
          ????????????notFull.signal();
          ????}

          2.9,clear()方法

          ????/**
          ?????*?Atomically?removes?all?of?the?elements?from?this?queue.
          ?????*?The?queue?will?be?empty?after?this?call?returns.
          ?????*/
          ????public?void?clear()?{
          ????????//方法上的注釋已經(jīng)說明了clear()方法的作用是干什么的了,很清晰
          ????????//在clear()操作時(shí),不允許進(jìn)行put/take操作,進(jìn)行了加鎖
          ????????fullyLock();
          ????????try?{
          ????????//循環(huán)遍歷隊(duì)列的每一個(gè)元素,將其節(jié)點(diǎn)node對(duì)應(yīng)的元素item置為null,觸發(fā)gc
          ????????????for?(Node?p,?h?=?head;?(p?=?h.next)?!=?null;?h?=?p)?{
          ????????????????h.next?=?h;
          ????????????????p.item?=?null;
          ????????????}
          ????????//隊(duì)首隊(duì)尾相同表明隊(duì)列為空了
          ????????????head?=?last;
          ???????//將隊(duì)列的容量capacity置為0
          ????????????if?(count.getAndSet(0)?==?capacity)
          ????????????????notFull.signal();
          ????????}?finally?{
          ???????//記得在這里釋放鎖
          ????????????fullyUnlock();
          ????????}
          ????}

          2.10, toArray()方法

          ?public?Object[]?toArray()?{
          ?????//禁止put/take操作,所以進(jìn)行加鎖,看下面的第二步含義
          ????????fullyLock();
          ????????try?{
          ?????//獲取隊(duì)列元素個(gè)數(shù)size
          ????????????int?size?=?count.get();
          ?????//創(chuàng)建大小為size的object數(shù)組,之所以為Object類型是因?yàn)镺bject對(duì)象的范圍最大,什么類型都可以裝下
          ????????????Object[]?a?=?new?Object[size];
          ????????????int?k?=?0;
          ?????//循環(huán)遍歷隊(duì)列的每一個(gè)元素,將其裝入到數(shù)組object里面
          ????????????for?(Node?p?=?head.next;?p?!=?null;?p?=?p.next)
          ????????????????a[k++]?=?p.item;
          ????????????return?a;
          ????????}?finally?{
          ?????//最后進(jìn)行釋放對(duì)應(yīng)的鎖,其實(shí)這里你也可以學(xué)到很多東西的,比如說加鎖,解鎖操作,為啥要放到finally語句塊呢等等等
          ?????//都會(huì)潛移默化的指導(dǎo)你編碼的過程
          ????????????fullyUnlock();
          ????????}
          ????}
          //第二步,注釋已經(jīng)很好的說明了這個(gè)方法的含義,就是阻止put和take操作的,所以進(jìn)行獲取對(duì)應(yīng)的鎖進(jìn)行加鎖操作
          ?????/**
          ?????*?Locks?to?prevent?both?puts?and?takes.
          ?????*/
          ????void?fullyLock()?{
          ????????putLock.lock();
          ????????takeLock.lock();
          ????}

          2.11,方法總結(jié)

          這里自己沒有對(duì)隊(duì)列的poll()方法,offer()方法進(jìn)行分析,是因?yàn)樗蛅ake(),add()方法的分析過程都太一樣了,至于一點(diǎn)點(diǎn)差別,你可以去自己看下源碼哈,這里由于篇幅的問題就不過多介紹了,其實(shí)整個(gè)方法的分析就是基于鏈表和數(shù)組的操作。不過這里沒有在方法的分析過程中去寫時(shí)間復(fù)雜度,不過,你看過vector源碼之后就知道如何分析了。

          三,總結(jié)一下

          3.1,思考一下

          文章的開頭拋出了下面的3個(gè)問題

          1.1,為什么要分析源碼? 那我這里在問下,你分析完這個(gè)集合源碼,學(xué)習(xí)到了什么?是不是有點(diǎn)啟發(fā)?

          1.2,分析源碼的好處是? 在分析的過程中,你學(xué)會(huì)了如何更加去編碼,或許你沒有意識(shí)到,但這是潛移默化的過程,相信你學(xué)習(xí)到了

          1.3,如何分析源碼? 在整個(gè)文章的閱讀過程中,想必你已經(jīng)學(xué)會(huì)了如何分析源碼了,從哪入手,這也是一種潛移默化的過程


          有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)

          歡迎大家關(guān)注Java之道公眾號(hào)


          好文章,我在看??

          瀏覽 47
          點(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>
                  国产无码高清视频 | 久久天天躁日日躁狠狠躁 | 免费看又色又爽又黄的成人用品 | 亚洲xxxxx | ww视频在线观看 |