首先是個人的一些閱讀源碼的小技巧,不一定適用每個人,可以直接跳過。 閱讀源碼的一些個人技巧 博客+總結 個人覺得大多數情況下跟著一篇優秀的博客配合著看就足夠了,之後再自己寫博客總結一遍加深印象,畫一下流程圖基本都能理順。(圖為學AQS時本人畫的獲取獨占鎖流程圖) 類關係 配合idea看類之間的關係( ...
首先是個人的一些閱讀源碼的小技巧,不一定適用每個人,可以直接跳過。
閱讀源碼的一些個人技巧
博客+總結
個人覺得大多數情況下跟著一篇優秀的博客配合著看就足夠了,之後再自己寫博客總結一遍加深印象,畫一下流程圖基本都能理順。(圖為學AQS時本人畫的獲取獨占鎖流程圖)
類關係
配合idea看類之間的關係(ctrl+alt+shift+u)的功能也能更好的理解整個項目的整體架構。因為很多源碼其實並不是真的複雜,只是為了擴展性優雅簡潔等原因建立了大量的介面和抽象類以及各種設計模式,使得項目看起來很龐大很複雜,藉助這個功能有利於你排除掉一些你暫時不想去關心的設計邏輯。知道那個部分才是最核心的邏輯,專註於去看核心代碼。
多看註釋
但是如果你看的博客裡面剛好缺少了一部分你想看的內容,而你又找不到資料,需要自己去看,又或者你想看的源碼一點點資料都找不到的情況下想去看源碼。
這個時候比較有作用的就是註釋,源碼中的註釋看不懂也沒關係,放到百度翻譯里基本也能理解大概的意思。仔細看完方法或類的註釋之後你就理解了接下來這個類大致是在做什麼,之後讀它的源碼會通順很多,有一些方法或類甚至在你看完註釋之後就已經能知道你想看的內容了,已經沒有需要繼續往下讀了。
不僅僅是類或方法的註釋文檔,方法中代碼的註釋也很重要,基本上稍微複雜一點點的代碼,甚至有時候加個鎖,作者都會認認真真的寫一行註釋解釋自己這麼做的原因。
適當囫圇吞棗
還有一點是適當忽略一些不重要的細節,這個主要看你想看什麼,一般我們看第一遍大多數只是想知道大致的流程是什麼樣的,所以可以適當忽略併發邏輯和一些方法里的內容(看一眼註釋先知道這個方法會做什麼的就夠了)。第一遍大致知道流程,第二第三遍再去深究細節和併發邏輯等。
善用debug
多用debug,很多時候源碼走的路線會和你想象中的有很大不同,你以為會進入這個if,其實他偷偷進了else。
短路機制
經常看到利用短路機制的代碼,這裡以 AbstractQueuedSynchronizer 的 acquire 方法為例子,只有 tryAcquire 獲取鎖失敗, !tryAcquire 返回 true 時才會執行後面進入阻塞隊列並掛起的方法,如果獲取鎖成功了,根據短路機制自然不會執行入隊方法。
// AbstractQueuedSynchronizer.acquire(int arg) if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) { selfInterrupt(); }
拆高低位
ReentrantReadWriteLock的這段代碼里將AQS的state一分為二給共用鎖和獨占鎖使用,個人覺得這種設計非常巧妙:
// ReentrantReadWriteLock abstract static class Sync extends AbstractQueuedSynchronizer { // 下麵這塊說的就是將 state 一分為二,高 16 位用於共用模式,低16位用於獨占模式 static final int SHARED_SHIFT = 16; static final int SHARED_UNIT = (1 << SHARED_SHIFT); static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 取 c 的高 16 位值,代表讀鎖的獲取次數(包括重入) static int sharedCount(int c) { return c >>> SHARED_SHIFT; } // 取 c 的低 16 位值,代表寫鎖的重入次數,因為寫鎖是獨占模式 static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
While(n-- > 0)和while (--n >= 0)
忘記在哪裡看到的了,翻了一下瀏覽記錄應該是在Java AIO部分的源碼里,這種寫法感覺很簡潔就記下來了,不過可讀性似乎不太高,特別是第一種乍一看還以為是lambda表達式
意思等同於 for (int i = 0; i < n; i++) ,但是 while(n-- > 0) 和 while (--n >= 0) 這種寫法會直接改變n的值
xx = null
在很多jdk的源碼中我們都可以看到 xx = null // help GC 這樣的代碼,用來置空引用,幫助jvm完成gc。具體可以瞭解可達性演算法。
這裡我們以LinkList為例子:
// LinkList 的方法 private E unlinkFirst(Node<E> f) { final E element = f.item; final Node<E> next = f.next; f.item = null; f.next = null; // help GC first = next; if (next == null) last = null; else next.prev = null; size--; modCount++; return element; }
位移運算符
在很多地方都會使用位移來進行運算,平時寫演算法題也一樣很多人都這麼使用,下麵以 ArrayList 的 grow 方法為例子,這裡通過右移1位使 oldCapacity 變為原來的0.5倍,之後加上它本身得到 newCapacity
// ArrayList.grow(int minCapacity) private void grow(int minCapacity) { // . . . . . . int newCapacity = oldCapacity + (oldCapacity >> 1);//newCapacity就是1.5倍的oldCapacity // . . . . . . }
以上是我目前的水平所能總結出來的,後續學到其他的會繼續更新,如果大家有什麼補充的請告訴我
最後慣例附一圖:(根本不存在想雇佣我的地方( ´_ゝ`).jpg)