设为首页 加入收藏

TOP

深入理解 ThreadLocal(一)
2019-09-14 00:53:03 】 浏览:147
Tags:深入 理解 ThreadLocal

前言

上篇文章 深入理解 Handler 消息机制 中提到了获取线程的 Looper 是通过 ThreadLocal 来实现的:

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

每个线程都有自己的 Looper,它们之间不应该有任何交集,互不干扰,我们把这种变量称为 线程局部变量 。而 ThreadLocal 的作用正是存储线程局部变量,每个线程中存储的都是独立存在的数据副本。如果你还是不太理解,看一下下面这个简单的例子:

public static void main(String[] args) throws InterruptedException {

    ThreadLocal<Boolean> threadLocal = new ThreadLocal<Boolean>();
    threadLocal.set(true);

    Thread t1 = new Thread(() -> {
        threadLocal.set(false);
        System.out.println(threadLocal.get());
    });

    Thread t2 = new Thread(() -> {
        System.out.println(threadLocal.get());
    });

    t1.start();
    t2.start();
    t1.join();
    t2.join();
    System.out.println(threadLocal.get());
}

执行结果是:

false
null
true

可以看到,我们在不同的线程中调用同一个 ThreadLocal 的 get() 方法,获得的值是不同的,看起来就像 ThreadLocal 为每个线程分别存储了不同的值。那么这到底是如何实现的呢?一起来看看源码吧。

以下源码基于 JDK 1.8 , 相关文件:

Thread.java

ThreadLocal.java

ThreadLocal

首先 ThreadLocal 是一个泛型类,public class ThreadLocal<T>,支持存储各种数据类型。它对外暴露的方法很少,基本就 get()set()remove() 这三个。下面依次来看一下。

set()

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t); // 获取当前线程的 ThreadLocalMap
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value); // 创建 ThreadLocalMap
}

这里出现了一个新东西 ThreadLocalMap,暂且就把他当做一个普通的 Map。从 map.set(this, value) 可以看出来这个 map 的键是 ThreadLocal 对象,值是要存储的 value 对象。其实看到这,ThreadLocal 的原理你应该基本都明白了。

每一个 Thread 都有一个 ThreadLocalMap ,这个 Map 以 ThreadLocal 对象为键,以要保存的线程局部变量为值。这样就做到了为每个线程保存不同的副本。

首先通过 getMap() 函数获取当前线程的 ThreadLocalMap :

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

原来 Thread 还有这么一个变量 threadLocals

/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class.
*
* 存储线程私有变量,由 ThreadLocal 进行管理
*/
ThreadLocal.ThreadLocalMap threadLocals = null;

默认为 null,所以第一次调用时返回 null ,调用 createMap(t, value) 进行初始化:

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

get()

set() 方法是向 ThreadLocalMap 中插值,那么 get() 就是在 ThreadLocalMap 中取值了。

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t); // 获取当前线程的 ThreadLocalMap
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result; // 找到值,直接返回
        }
    }
    return setInitialValue(); // 设置初始值
}

首先获取 ThreadLocalMap,在 Map 中寻找当前 ThreadLocal 对应的 value 值。如果 Map 为空,或者没有找到 value,则通过 setInitialValue() 函数设置初始值。

private T setInitialValue() {
    T value = initialValue(); // 为 null
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

protected T initialValue() {
    return null;
}

setInitialValue()set() 逻辑基本一致,只不过 value 是 null 而已。这也解释了文章开头的例子会输出 null。当然,在 ThreadLocal 的子类中,我们可以通过重写 setInitialValue() 来提供其他默认值。

remove()

public void remove() {
    ThreadLocalMap m = getMap(Thread.currentThread());
    if (m != null)
        m.remove(this);
}

remove() 就更简单了,根据键直接移除对应条目。

看到这里,ThreadLocal 的原理好像就说完了,其实不然。ThreadLocalMap 是什么样的一个哈希表呢?它是如何解决哈希冲突的?它是如何添加,获取和删除元素的?可能会导致内存泄露吗?

其实 ThreadLocalMap 才是 ThreadLocal 的核心。ThreadLocal 仅仅只是提供给开发者的一个工具而已,就像 Handler 一样。带着上面的问题,来阅读 ThreadLocalMap 的源码,体会 JDK 工程师的鬼斧神工。

ThreadLocalMap

Entry

ThreadLocalMap 是 ThreadLocal 的静态内部

首页 上一页 1 2 3 4 5 下一页 尾页 1/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Android设计模式—观察者模式 下一篇Android源码阅读技巧--查找开发者..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目