美文网首页Java学习笔记
ThreadLocal In Java 8

ThreadLocal In Java 8

作者: 卑鄙的鹿尤菌 | 来源:发表于2017-05-21 16:22 被阅读458次

    TheadLocal 是一个线程内部的数据存储类,通常用于存储以线程为作用域的数据变量,避免产生多线程的同步问题。
    记得上学时候也写过相关源码的分析文章,但今天翻看Java 8中的ThreadLocal类时发现它被重构了,因此也重读一下相关源码实现。
    ThreadLocal是一个泛型类,定义如下:

    public class ThreadLocal<T>
    

    作为存储类,我们抓住主要矛盾从setget方法开始分析。

    ThreadLocal 存数据 —— set方法


        public void set(T value) {
            //获取到当前线程
            Thread t = Thread.currentThread();
            //根据当前线程获取ThreadLocalMap对象
            ThreadLocalMap map = getMap(t);
            
            if (map != null) // map对象不为空则存入对象
                map.set(this, value);
            else // 否则创建ThreadLocalMap
                createMap(t, value);
        }
    

    可以看到,set方法向一个ThreadLocalMap对象中存入数据,ThreadLocalMapThreadLocal的静态内部类,具体实现我们随后进行分析。我们现在只需知道它被用来存储数据(像HashMap那样):key是线程对应的threadlocal对象,value是要存入的数据对象。

    作者的叨叨:其实看到这里机智点的小伙伴可以尝试猜测整个ThreadLocal类的实现原理。
    先别管这样的猜测是不是正确,这不重要。其实我们看源码的时候要养成主动思考的习惯,假如让你来设计或者实现这样的方案,你会怎么设计?然后再与实际源码对比一下,并且关注一下源码的细节。

    下面我们接着看一下如何获取之前存入的数据对象。

    ThreadLocal 取数据 —— get方法


        public T get() {
            //获取到当前线程
            Thread t = Thread.currentThread();
            //获取ThreadLocalMap对象
            ThreadLocalMap map = getMap(t);
            if (map != null) {
            //获取ThreadLocalMap中的Entry对象并拿到Value
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            //如果之前未创建过ThreadLocalMap,则返回null
            return setInitialValue();
        }
    

    从get方法中我们可以看到,如果当前线程之前向ThreadLocalMap存入过数据对象,则根据线程实例获取到存入的数据对象。

    看完了存取两个动作,让我们揭开ThreadLocalMap的面纱,看看ThreadLocal是如何存储数据对象的。

    ThreadLocal 储数据 —— ThreadLocalMap


    从上面的set和get方法中都可以看到,ThreadLocal一直在对ThreadLocalMap进行操作。我们来搞清楚Thread,ThreadLocal以及ThreadLocalMap三者之间的关系。
    在Thread类中可以看到,每个Thread对象中都持有一个ThreadLocalMap成员变量:

     ThreadLocal.ThreadLocalMap threadLocals = null;
    

    在上面的set方法中我们看到了对ThreadLocalMap的创建:

        void createMap(Thread t, T firstValue) {
            //创建一个ThreadLocalMap对象赋值给当前线程的成员变量threadLocals
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
        ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }
    

    从构造函数中我们可以看到ThreadLocalMap中维护了一个Entry对象的数组table:

    private Entry[] table;
    static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;
    
            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }
    

    Entry对象是一种类似<ThreadLocal,Object>键值对的结构,每个ThreadLocal对象都对应了各自的数据对象。现在Thread,ThreadLocal以及ThreadLocalMap三者之间的关系就清楚了:

    relationship.png

    通过ThreadLocalMap和Thread的一一对应关系实现了线程作用域中的数据对象存取。

    相关文章

      网友评论

        本文标题:ThreadLocal In Java 8

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