美文网首页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