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

来源:互联网 发布:sqlserver清空数据库 编辑:程序博客网 时间:2024/06/11 02:37

从头认识多线程-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

0 0