如何阅读jdk源码?

🖕欢迎关注个人公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一块儿畅游源码的海洋。java


简介

这篇文章主要讲述jdk自己的源码该如何阅读,关于各类框架的源码阅读咱们后面再一块儿探讨。git

笔者认为阅读源码主要包括下面几个步骤。面试

设定目标

凡事皆有目的,阅读源码也是同样。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