一张图看懂ThreadLocal原理

一张图看懂ThreadLocal原理_第1张图片
ThreadLocal原理

图解:该图基于Android中的ThreadLocal在Looper中的应用,其能够实现一个线程只有一个Looper的私有实例,左边是通过代码分析得到的类关系图,我们可以看到可以通过线程得到一个Looper,首先通过Thread里的成员变量得到ThreadLocalMap,然后通过Looper中的静态变量能够得到ThreadLocals如下代码,

一张图看懂ThreadLocal原理_第2张图片
image.png

然后通过ThreadLocals实例得到Map中的Entry然后在通过该实例得到Looper的实例,如下
一张图看懂ThreadLocal原理_第3张图片
image.png

另外,右图是在该应用中的各种类的引用关系链,通过它我们可以分析到为什么Entry引用key是用的弱引用。我们在android中有可能会用到Looper.quit简单讲就将Looper实例置为null让垃圾回收器回收。此时分析右图,可知,当Looper为null时,ThreadLocal的实例也就没有太大的用处了,也需要进行回收,但是如果Entry引用key是用的强应用,ThreadLocal的实例就不能回收,如果是弱引用的话,就可以在垃圾回收GC的时候强行回收掉。但仅仅是这样还不行,在某些情况下还是会出现问题,这就需要具体情况具体分析,下面看一个来自文章 https://mp.weixin.qq.com/s/vURwBPgVuv4yGT1PeEHxZQ的例子:
一张图看懂ThreadLocal原理_第4张图片
image.png

分析该例子的引用链如下
一张图看懂ThreadLocal原理_第5张图片
image.png

TestClass的实例置null是想释放int的空间,但是不好意思,我还是能够有引用链到达int,通过Thread->Entry->TestClass->int,可能有人问TestClass实例不是置null了么,TestClass的实例t,只是线程中Thread中的一个引用置null了,也就是说thread->TestClass这条线不可达,但是Thread->Entry->TestClass->int是可达的,所以会内存泄露,与是内存溢出。解决方案是 remove函数,即溢出Entry对TestClass的引用,源码如下:

 private void remove(ThreadLocal key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }
  public void clear() {
        this.referent = null;
    }

你可能感兴趣的:(一张图看懂ThreadLocal原理)