从头认识多线程-4.4 ThreadLocal的实现原理

从头认识多线程-4.4 ThreadLocal的实现原理

这一章节我们来讨论一下ThreadLocal的实现原理。

1.ThreadLocal的存储

一般可以认为是ThreadLocalMap这个内部类来存储,但是如果更加深入一层,其实他的存储是Entry类的可变数组,这个跟HashMap的存储比较相似

2.为什么在使用ThreadLocal的时候需要初始化?

ThreadLocal的初始化源码:

 /**
     * Returns the current thread's "initial value" for this
     * thread-local variable.  This method will be invoked the first
     * time a thread accesses the variable with the {@link #get}
     * method, unless the thread previously invoked the {@link #set}
     * method, in which case the <tt>initialValue</tt> method will not
     * be invoked for the thread.  Normally, this method is invoked at
     * most once per thread, but it may be invoked again in case of
     * subsequent invocations of {@link #remove} followed by {@link #get}.
     *
     * <p>This implementation simply returns <tt>null</tt>; if the
     * programmer desires thread-local variables to have an initial
     * value other than <tt>null</tt>, <tt>ThreadLocal</tt> must be
     * subclassed, and this method overridden.  Typically, an
     * anonymous inner class will be used.
     *
     * @return the initial value for this thread-local
     */
    protected T initialValue() {
        return null;
    }

其实我们一打开源码就不难看见,其实在初始化之初,直接就是返回null,所以在没有初始化的情况下,直接get里面的元素,是返回null

3.set方法的实现

源码:

/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to 
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    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作为容器,然后把值放到里面去,我们在进入ThreadLocalMap去看看
ThreadLocalMap是在ThreadLocal里面实现的内部类,由于代码太长这里就不贴出来,笔者曾看到有文章说ThreadLocalMap跟HashMap的实现是一样,然后仔细对比ThreadLocalMap跟HashMap的实现,是非常的相似,都是使用Entry数组来实现存储,然后遍历里面的元素,估计当时设计的思路都差不多,但是HashMap在Entry的实现上,明显比ThreadLocalMap的Entry要强。

4.get方法的实现,下面将解释为什么没有初始化,返回null

源码:

 /**
     * Returns the value in the current thread's copy of this
     * thread-local variable.  If the variable has no value for the
     * current thread, it is first initialized to the value returned
     * by an invocation of the {@link #initialValue} method.
     *
     * @return the current thread's value of this thread-local
     */
    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();
    }

讲到get方法,我们深入debug,发现:
我们点击进去setInitialValue方法,

/**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    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;
    }

可以看到他返回的是value,而value,也就是初始化方法initialValue()的返回值,这里就解释了为什么没有初始化的时候,get返回的是null

5.为什么多线程情况下,每一个线程只会计算自己工作内存里面的变量副本?

我们分别看看set和get方法里面的某一两句
set方法

Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);

get方法

Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);

getMap方法

  /**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

在两个方法里面,一开始都会出现这两句代码,这里我们点击去getmap方法,看见,里面出现了t.threadLocals,使用变量副本的原因就在这一句里面。

6.remove方法

源码:

/**
     * Removes the current thread's value for this thread-local
     * variable.  If this thread-local variable is subsequently
     * {@linkplain #get read} by the current thread, its value will be
     * reinitialized by invoking its {@link #initialValue} method,
     * unless its value is {@linkplain #set set} by the current thread
     * in the interim.  This may result in multiple invocations of the
     * <tt>initialValue</tt> method in the current thread.
     *
     * @since 1.5
     */
     public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

remove方法主要是去掉某个线程的变量副本,使得这个线程不能够在使用变量副本进行计算。

总结:我们这一章节简单的描述了ThreadLocal的实现原理。

这一章节就到这里,谢谢。


我的github:https://github.com/raylee2015/DeepIntoThread

目录:http://blog.csdn.net/raylee2007/article/details/51204573

©️2020 CSDN 皮肤主题: 酷酷鲨 设计师:CSDN官方博客 返回首页