如何閱讀jdk源碼?

簡介

這篇文章主要講述jdk自己的源碼該如何閱讀,關於各類框架的源碼閱讀咱們後面再一塊兒探討。java

筆者認爲閱讀源碼主要包括下面幾個步驟。面試

設定目標

凡事皆有目的,閱讀源碼也是同樣。spring

從大的方面來講,咱們閱讀源碼的目的是爲了提高本身的技術能力,運用到工做中,遇到問題快速定位,升職加薪等等。數組

從小的方面來講,閱讀某一段源碼的目的就是要搞清楚它的原理,就是死磕,就是那種探索真相的執拗。緩存

目的是抽象的,目標是具體的,咱們閱讀源碼以前必定要給本身設定一個目標。安全

好比,下一章咱們將要一塊兒學習的ConcurrentHashMap,咱們能夠設定如下目標:數據結構

(1)熟悉ConcurrentHashMap的存儲結構;多線程

(2)熟悉ConcurrentHashMap中主要方法的實現過程;併發

(3)探索ConcurrentHashMap中出現的新技術;框架

提出問題

有了目標以後,咱們要試着提出一些問題。

仍是以ConcurrentHashMap爲例,筆者提出瞭如下這些問題:

(1)ConcurrentHashMap與HashMap的數據結構是否同樣?

(2)HashMap在多線程環境下什麼時候會出現併發安全問題?

(3)ConcurrentHashMap是怎麼解決併發安全問題的?

(4)ConcurrentHashMap使用了哪些鎖?

(5)ConcurrentHashMap的擴容是怎麼進行的?

(6)ConcurrentHashMap是不是強一致性的?

(7)ConcurrentHashMap不能解決哪些問題?

(8)ConcurrentHashMap除了併發安全,還有哪些與HashMap不一樣的地方,爲何要那麼實現?

(8)ConcurrentHashMap中有哪些不常見的技術值得學習?

如何提出問題

不少人會說,我也知道要提出問題,可是該怎麼提出問題呢?

這確實是很困難的一件事,筆者認爲主要是三點:

(1)問本身

把本身當成面試官問本身,往死裏問的那種。

若是問本身問不出幾個問題,也沒關係,請看下面。

(2)問互聯網

不少問題可能本身也想不到,那就須要上網大概查一下相關的博客,看人家有沒有提出什麼問題。

或者,查詢相關面試題。

好比,筆者學習ConcurrentHashMap這個類時,上網一查不少都是基於jdk7的,那這時候就能夠提出一個問題,jdk8與jdk7中ConcurrentHashMap這個類的實現方式有何不一樣?jdk8對jdk7做了哪些優化?

(3)不斷髮現問題

在源碼閱讀的過程當中,可能看着看着就遇到個問題,這是很是常見的,這種問題也應該保留下來研究研究。

好比,ConcurrentHashMap中size()方法是怎麼實現的?@sun.misc.Contended這玩意是什麼鬼東西?而後上網一查,與是爲了不僞共享,我X,僞共享又是啥?而後你再查一下僞共享,又出來了CPU多級緩存?學完CPU多級緩存,是否是以爲跟jvm的內存模型很像?問完這一連串問題,是否是感受世界都清晰了?^_^

看吧,問題是源源不斷地被發現的。

因此,一開始提不出幾個問題也沒關係,關鍵是要看,看了才能發現更多的問題。

帶着問題閱讀源碼,忽略沒必要要的細節,死磕重要的細節

首先,必定要帶着問題閱讀源碼。

其次,必定要忽略沒必要要的細節。

再次,必定要死磕重要的細節。

乍一看,後面兩步彷佛有所矛盾,其實否則,忽略沒必要要的細節是爲了避免迷失在源碼的世界中,死磕重要的細節是爲了弄清楚源碼的真相。

這裏的細節是忽略仍是死磕,主要是看跟問題的相關性。

jdk源碼仍是比較好閱讀的,若是後面看spring的源碼,作不到忽略沒必要要的細節,真的是會迷失的,先埋個伏筆哈~~

舉個例子,以前閱讀過ArrayList的序列化相關的代碼中的readObject()方法。

s.readInt();這行是幹嗎的?省略行不行?這時候就要去了解序列化相關的知識,而後看看writeObject()裏面的實現,這就是要死磕的代碼。

SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);這行又是幹嗎的?乍一看,好像是跟權限相關的代碼,跟咱們的問題「序列化」無關,忽略之,若是實在想知道,先打個標記,等把序列化的問題解決了再來研究這個東西。

private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
    // 聲明爲空數組
    elementData = EMPTY_ELEMENTDATA;

    // 讀入非transient非static屬性(會讀取size屬性)
    s.defaultReadObject();

    // 讀入元素個數,沒什麼用,只是由於寫出的時候寫了size屬性,讀的時候也要按順序來讀
    s.readInt();

    if (size > 0) {
        // 計算容量
        int capacity = calculateCapacity(elementData, size);
        SharedSecrets.getJavaOISAccess().checkArray(s, Object[].class, capacity);
        // 檢查是否須要擴容
        ensureCapacityInternal(size);
        
        Object[] a = elementData;
        // 依次讀取元素到數組中
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

多作比較

在閱讀jdk源碼的時候,還有很重要的一點,就是要多作比較,比較也能夠分爲橫向比較和縱向比較。

(1)橫向比較

就是與類似的類作比較。好比,集合模塊中,基本都是各類插入、查詢、刪除元素,那這時候能夠從數據結構、時間複雜度等維度進行比較,這就是橫向比較。

(2)縱向比較

能夠從集合發展的歷史進行比較。好比,HashMap的發展史,從(單個數組)實現(沒錯,能夠直接用一個數組實現HashMap),到(多數組+鏈表)實現,再到jdk8中的(多數組+鏈表+紅黑樹)實現,這就是縱向比較。

多作實驗

最後一步,最最最最重要的就是要多作實驗。

好比,ConcurrentHashMap是否是強一致性的?

能夠啓動多個線程去不斷調用get()、put()、size()方法,看看是否是強一致性的。

耐心&堅持

這一點我很少說,你們都懂得。

不論是什麼領域,耐心&堅持都是最難能難得的品質。

閱讀源碼也是同樣,只要耐心地堅持下去,終將有所收穫。

彩蛋

哎呀,一不當心透露了下一章ConcurrentHashMap的內容。

你們能夠用本篇所說的方法試着閱讀一下ConcurrentHashMap的源碼,下一章咱們再一塊兒學習哈哈~~


qrcode

相關文章
相關標籤/搜索