java.lang.ThreadLocal 源码解析

作者: 9b273c1ef2ee | 来源:发表于2017-03-19 20:06 被阅读95次

    原文发表于:http://blog.csdn.net/qq_27485935 , 大家没事可以去逛逛 (ง •̀_•́)ง

    ThreadLocal

    概述: 位于 java.lang 包下, 是一个线程内部的数据存储类, 通过它可以在指定线程中存储数据, 数据存储之后, 只有在指定线程中才可以获取到存储的数据。

    使用场景: Looper、 ActivityThread

    基本用法:

    mThreadLocal = new ThreadLocal<Integer>();
    mThreadLocal.set(10);  // 通过 set 方法设值
    mThreadLocal.get();  // 通过 get 方法取值
    

    源码解析

    ThreadLocal#set

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);  // ①
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }
    

    ① ThreadLocal#getMap

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

    我们一步步分析, 首先通过 getMap() 方法获取 Thread 内部 的 threadLocals 对象。 而 ThreadLocalMap 为 ThreadLocal 内部的静态类。

    ThreadLocalMap

    static class ThreadLocalMap {
    
        static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;
    
            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
        
        private Entry[] table;
        
        
        private void set(ThreadLocal key, Object value) {
                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)]) {
                    ThreadLocal k = e.get();
    
                    if (k == key) {
                        e.value = value;
                        return;
                    }
    
                    if (k == null) {
                        replaceStaleEntry(key, value, i);
                        return;
                    }
                }
    
                tab[i] = new Entry(key, value);
                int sz = ++size;
                if (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }
    }
    

    源码可见, ThreadLocalMap 通过 Entry 数组来保存键值对的, 并且对键也是弱引用状态。 set 一个数值, 将会取出键(ThreadLocal) 中的 threadLocalHashCode 值, 而 threadLocalHashCode 是唯一的, 所以可以通过它来确定数值在 Entry 数组中的位置。

    所以回到刚刚的 ThreadLocal#set 方法。 先从 Thread 类中取出维持线程本地数据的 ThreadLocalMap 类, 若 map 对象不为空, 直接将 ThreadLocal 和数据作为参数 set 到 map 中维持的 table 数组中。 若 map 对象为空, 调用 createMap 方法, 创建 ThreadLocalMap 对象, 设入数据并将该对象设置到 Thread 对象当中。 以下为 createMap 源码。

    ThreadLocal#createMap

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

    接下来分析 get 方法

    ThreadLocal#get

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null)
                return (T)e.value;
        }
        return setInitialValue();
    }
    
    

    同样, 获取当前线程对象, 并获取维持线程本地数据的 threadLocals, 以 ThreadLocal 对象取出对应数值对象。 如果对象不为空, 就强制转化为泛型 T 对应的类型。 若 map 为空, 或者键值对取出数据对象为空, 就调用 setInitialValue 方法, 以下代码就不解释了。

    ThreadLocal#setInitialValue

    private T setInitialValue() {
        T value = initialValue();
        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;
    }
    

    从 ThreadLocal 的 setget 方法可以看出, 它们所操作的对象都是当前线程的 threadLocals 对象中的 table 数组, 因此在不同线程中访问同一个 ThreadLocal 的 setget 方法, 他们对 ThreadLocal 所做的读/写操作仅限于各个线程的内部, 获取的值也就不同了。

    相关文章

      网友评论

        本文标题:java.lang.ThreadLocal 源码解析

        本文链接:https://www.haomeiwen.com/subject/xzdvnttx.html