美文网首页
ThreadLocal

ThreadLocal

作者: 86棵梦 | 来源:发表于2017-05-27 12:43 被阅读0次

    这个东东大家可能平时会听说过,但是了解可能不是很多,当然网上也有了很多介绍文章,这里我只按照我的理解来说一下。知道这个东西应该挺多都是看 Looper 看到的。
    我们先看看这个东西都是怎么用的:

    private final Handler uiThreadHandler = new Handler(Looper.getMainLooper());
    ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
    int mainThreadNum = 1;int workerThreadNum = 2;
    public void testThreadLocal() {
        threadLocal.set(mainThreadNum);
        new Thread(new Runnable() {
            @Override
            public void run() {
                threadLocal.set(workerThreadNum);
                uiThreadHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.d("UIThread", "" + threadLocal.get());
                    }
                });
                Log.d("WorkerThread", "" + threadLocal.get());
            }
        }).start();
        Log.d("UIThread", "" + threadLocal.get());
    }
    

    这里的输出

    UIThread: 1
    WorkerThread: 2
    UIThread: 1
    

    可见,ThreadLocal 的功能就是在不同 Thread set 进去的数据不会相互干扰并且可以直接获取到。

    然后我们说一下这个东西是怎么实现的:

    public class ThreadLocal<T> {
        private final int threadLocalHashCode = nextHashCode();
        private static AtomicInteger nextHashCode = new AtomicInteger();
        public ThreadLocal() {  }
        ...
        static class ThreadLocalMap {
            ...
        }
    }
    

    不管别的,先看构造函数与成员变量。由上可知,可以随意构造实例,并且成员变量只有一个 threadLocalHashCode,所以很明显的可以知道这货就是个皮包公司,它并不存储任何实例,只是包含了一个这个实例的索引,也就是threadLocalHashCode

    那接下来我们在看看它的成员函数,这里只那 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);
    }
    
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
    

    这里我们可能就比较清晰为什么 ThreadLocal 可以提供这种可以在不同线程存入/读取数据而互不干扰的功能了。
    就是因为实际的变量并不是存入到了 ThreadLocal 中,而是存入到了这个操作对应的 Thread.threadLocals 中了(可以看上边的 getMap 函数)。而且这个 Thread.threadLocals 的实例化也是在 ThreadLocal 的代码中(createMap 函数)。

    这个变量在 Thread 中的定义:

    public class Thread implements Runnable {
        ...
        ThreadLocal.ThreadLocalMap threadLocals = null;
        ...
    }
    

    就是一个引用而已,那现在就指向了 ThreadLocalMap 这个数据结构。

    static class ThreadLocalMap {
        static class Entry extends WeakReference<ThreadLocal> {
        Object value;
        Entry(ThreadLocal k, Object v) {
            super(k);
            value = v;
        }
    }
    
    private static final int INITIAL_CAPACITY = 16;
    private Entry[] table;ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
    
        table = new Entry[INITIAL_CAPACITY];
        int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
        table[i] = new Entry(firstKey, firstValue);
        ...
    }}
    

    这里边最主要的其实就是这个数组 Entry[] table,这个 Entry 的定义也在上边了,其实就是一个包含了具体变量的 ThreadLocal 的弱引用。
    而 threadLocalHashCode 这个变量就是计算具体变量在这个数组中的索引的。

    ok,整个的东西已经串下来了。

    当然,这里边疑问还有很多,比如 threadLocalHashCode 这个具体的计算方式,这个 table 数组是如何扩容等问题,大家可以自行看代码了。

    相关文章

      网友评论

          本文标题:ThreadLocal

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