Android进阶之路Android技术知识Android开发经验谈

ThreadLocal到底是个什么东西?

2019-12-08  本文已影响0人  我爱纽约先生
Threads

大家在学习Android原代码的时候,经常会看到ThreadLocal这个东西。这个东西日常开发的时候用的不多,有点点神秘。今天这篇文章就帮你揭开ThreadLocal的面纱,了解它的真相。


对ThreadLocal最简单的理解

从使用者的角度,可以把ThreadLocal简单看成是一个Map:Key的类型是Thread,Value是任意你想要保存的值。类似这样:Map<Thread, Object>

可是,如果说ThreadLocal是个Map<Thread, Object>类型的话,那它提供的接口应该是这样的:

void set(Object key, Object value)
Object get(Object key)

而实际上ThreadLocal的接口是这样的:

void set(Object value)
Object get()

看到没?和普通map的读写接口不一样:ThreadLocal的读写接口里面并没有传key值!既然没有传key,那对于一个map来说,该如何读写value呢?

其实,ThreadLocal的这个map有点特殊,它不允许随便传一个Thread对象作为Key值来访问,只能传递当前的线程对象。而当前的线程对象可以通过静态方法Thread.currentThread();来获得,所以就不用传key这个参数了。

用代码总结下刚才的逻辑:

public void set(Object value){
    Thread t = Thread.currentThread(); //获得当前线程
    map.set(t, value); //以当前线程为key,设置value
}

public Object get(){
    Thread t = Thread.currentThread(); //获得当前线程
    return map.get(t); //以当前线程为key,获得value
}

以上只是对ThreadLocal最简单的理解,如果你不追求实现细节的话,这基本也够了,如果你想了解更多的话,请接着看下面的内容。


更深入一点了解ThreadLocal

在我们没有阅读ThreadLocal源码的时候,我以为ThreadLocal一定是里面维护着一个map对象,保存着所有Thread对象和Value的值,如下图所示:

我想象中的ThreadLocal的实现方式

可是,在我读过源码之后,才发现:我想错了。

实际上,ThreadLocal本身并没有存储任何对象,所有的东西都存储在Thread对象本身里!

首先,在Thread对象(注意,不是ThreadLocal!)里有个成员变量:

ThreadLocal.ThreadLocalMap threadLocals = null;

这个成员变量的类型是ThreadLocal.ThreadLocalMap,这是ThreadLocal的一个内部类,可以理解为一个简单的HashMap(关于这个内部类的实现本篇就不详细展开了,有兴趣的同学可以看源码),就是保存键值对的一个容器。

其实,所有set到ThreadLocal上的对象,实际都保存在当前Thread中的threadLocals成员变量里。

为了说的清楚,下面我用伪代码的形式,写一下ThreadLocal的Set()方法的核心逻辑:

public void set(T value) {
        Thread t = Thread.currentThread(); //先获得当前线程
        if (t.threadLocals != null){ //判断当前线程上的threadLocals成员是否为空
            t.threadLocals.set(this, value); //如果threadLocals不等于空,则set value,ThreadLocal对象作为key值。
        }else{
            t.threadLocals = new ThreadLocalMap(); //如果threadLocals为空,则创建之然后再set value。
            t.threadLocals.set(this, value); 
        }
    }

通过以上代码大家应该都能看清楚,真正的value值是保存在Thread对象上的,同时,key值是ThreadLocal对象。

再看看get方法,依然是只有核心逻辑的伪代码:

public T get() {
        Thread t = Thread.currentThread(); //先获得当前线程
        if (t.threadLocals != null) { //判断当前线程上的threadLocals成员是否为空
            return (T)t.threadLocals.get(this); //不为空,则以ThreadLocal对象作为key值得到value,然后返回。
        }
        return setInitialValue(); //返回初始值
    }

理清了get和set方法,相信整个ThreadLocal的实现方式,大家应该都比较清楚了。

总之,ThreadLocal的存储方式,大概是这个样子:


ThreadLocal的存储结构

为什么如此实现?

作者之所以这样设计,我相信是经过了充分考虑的。我能想到的一点,就是考虑了对象的生命周期。

ThreadLocal要为每个线程维护value值,所以它的生命周期一般是长于线程对象生命周期的。一个长生命周期的对象维护一个短生命周期对象的引用,就有可能内存泄露,除非开发者要手动remove掉。

而把value存放在Thread上,一切就可以完美的解决。因为这个value对象是专门为Thread对象而存在的,所以value对象和Thread对象的生命周期应该完全一致。把value对象存放在Thread对象里,则不会有内存泄露的风险。虽然,Thread会维护一个ThreadLocal的引用(实际上是个弱引用),但是如前所说,一个短生命周期的对象维护长生命周期对象的引用,一般没什么问题。

几种对象生命周期长度比较

以上就是我对ThreadLocal的一些简单理解,欢迎大家指正。

上一篇下一篇

猜你喜欢

热点阅读