不同线程调用相同的 Looper.myLooper(),其实内部是调用了 ThreadLocal 的 get() 方法,而 get() 方法则在一开始就先获取当前线程的对象,然后直接通过包权限获取当前线程的 threadLocals 成员变量,该变量是一个 ThreadLocal 的内部类 ThreadLocalMap 对象,初始值为 null。
以上,是我们到目前所梳理的信息,虽然我们还不知道 ThreadLocalMap 作用是什么,但不妨碍我们对其进行猜测啊。如果这个类是用于存储数据的,那么一切是不是就可以说通了!
为什么不同线程中明明调用了同一对象的同一方法,却可以返回各自线程对应的数据呢?原来,这些数据本来就是存储在各自线程中了,ThreadLocal 的 get() 方法内部其实会先去获取当前的线程对象,然后直接将线程存储的容器取出来。
所以,我们来验证一下,ThreadLocalMap 是不是一个用于存储数据的容器类:
//ThreadLocal$ThreadLocalMap static class ThreadLocalMap { static class Entry extends WeakReference<ThreadLocal> { Object value; } private Entry[] table; private void set(ThreadLocal key, Object value) { ... } private Entry getEntry(ThreadLocal key) { ... } }猜对了,很明显,ThreadLocalMap 就是一个用于存储数据的容器类,set 操作,get 操作,连同容器数组都有了,这样一个类不是用于存储数据的容器类还是什么。至于它内部的各种扩容算法,hash 算法,我们就不管了,不深入下去了,知道这个类是干嘛用的就够了。当然,感兴趣你可以自行深入研究。
那么,好,我们回到最初的 ThreadLocal 的 get() 方法中继续分析:
//ThreadLocal#get() public T get() { //1. 获取当前的线程 Thread t = Thread.currentThread(); //2. 以当前线程为参数,获取一个 ThreadLocalMap 对象 ThreadLocalMap map = getMap(t); if (map != null) { //3. map 不为空,则以当前 ThreadLocal 对象实例作为key值,去map中取值,有找到直接返回 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } //4. map 为空或者在map中取不到值,那么走这里,返回默认初始值 return setInitialValue(); }第 1 步,第 2 步我们已经梳理清楚了,就是去获取当前线程的数据存储容器,也就是 map。拿到容器之后,其实也就分了两条分支走,一是容器不为 null,一是容器为 null 的场景。我们先来看看容器为 null 场景的处理:
//ThreadLocal#setInitialValue() private T setInitialValue() { //1. 获取初始值,默认返回Null,允许重写 T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else //2. 创建线程t的数据存储容器:threadLocals createMap(t, value); //3. 返回初始值 return value; }首先会通过 initialValue() 去获取初始值,默认实现是返回 null,但该方法允许重写。然后同样去获取当前线程的数据存储容器 map,为null,所以这里会走 createMap(),而 createMap() 我们之前分析过了,就是去创建参数传进去的线程自己的数据存储容器 threadLocals,并将初始值保存在容器中,最后返回这个初始值。
那么,这条分支到这里就算结束了,我们回过头继续看另一条分支,都跟完了再来小结:
//ThreadLocal#get() public T get() { //1. 获取当前的线程 Thread t = Thread.currentThread(); //2. 以当前线程为参数,获取一个 ThreadLocalMap 对象 ThreadLocalMap map = getMap(t); if (map != null) { //3. map 不为空,则以当前 ThreadLocal 对象实例作为key值,去map中取值,有找到直接返回 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } //4. map 为空或者在map中取不到值,那么走这里,返回默认初始值 return setInitialValue(); }另一条分支很简单,也就是如果线程的数据存储容器不为空,那么就以当前 ThreadLocal 对象实例作为 key 值,去这个容器中寻找对应的数据,如果有找到直接返回,没找到,那么就走 setInitialValue(),该方法内部会去取默认的初始值,然后以当前 ThreadLocal 对象实例作为 key 值存入到当前线程的数据存储容器中,并返回初始值。
到这里,get() 的流程已经梳理完毕了,那就先来小结一下:
当不同的线程调用同一个 ThreadLocal 对象的 get() 方法时,内部其实是会先获取当前线程的对象,然后通过包权限直接获取对象的数据存储容器 ThreadLocalMap 对象,如果容器为空,那么会新建个容器,并将初始值和当前 ThreadLocal 对象绑定存储进去,同时返回初始值;如果容器不为空,那么会以当前 ThreadLocal 对象作为 key 值去容器中寻找,有找到直接返回,没找到,那么以同样的操作先存入容器再返回初始值。