java.lang.ThreadLocal实现原理和源码分析
发布日期:2021-06-30 17:51:31 浏览次数:2 分类:技术文章

本文共 4642 字,大约阅读时间需要 15 分钟。

java.lang.ThreadLocal实现原理和源码分析

1ThreadLocal的原理:为每一个线程维护变量的副本。某个线程修改的只是自己的副本。

2ThreadLocal是如何做到把变量变成副本的呢?不是clone,而是简单地new。具体一点就是对于变量A,每一个线程都会执行一个初始化方法initialValue(),这个方法在ThreadLocal类的源码中只是返回null(下面代码),需要用户覆写,也就是在这个方法里面new一个A并返回,这个就是我们当前线程的副本。具体分析如下:

protected T initialValue() {        return null;}

我们看一个例子:我们顺着例子中的main函数的执行过程来作为我们分析的思路

import java.util.ArrayList;import java.util.List;/** * Created by liangyh on 2/25/16. */public class ThreadLocalTest {    private ThreadLocal
> threadLocal = new ThreadLocal
>(){ @Override protected List
initialValue() { List
list = new ArrayList<>(); list.add("a"); return list; } }; public String get(int index){ return threadLocal.get().get(index); } public void add(String value){ threadLocal.get().add(value); } public synchronized void print(){ List
list = threadLocal.get(); if(list != null){ for (int i = 0; i

1、我们先实例化一个ThreadLocalTest对象,然后调用print()方法。在print()方法中第一行调用了threadLocal.get()方法。返回的是当前线程的副本,现在的线程是主线程。get()方法干了什么工作了?

先看get方法的源码!它首先获得当前线程的对象t,然后根据t作为参数得到一个map,这个map我们下面会讲到,它的keyThreadLocal对象,value是变量副本。如果得到map不为空,则返回map中的value值,如果为空,进入setInitialValue()方法中。在这个get方法中我们还应该注意到一个方法,getMap(Threadt),它返回的是Thread对象的一个属性ThreadLocal.ThreadLocalMapthreadLocals =null;这个属性的作用是保存这个线程的变量副本的。ThreadLocalMap便是上面提到的map。刚开始的时候,这个threadLocals变量是空的,什么时候赋值呢?暂时不管,先把思路回到get方法中的最后一行代码。

/**     * 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 the map associated with a ThreadLocal. Overridden in     * InheritableThreadLocal.     *     * @param  t the current thread     * @return the map     */    ThreadLocalMap getMap(Thread t) {        return t.threadLocals;    }
 

程序执行到这里我们还没有拿到我们的值,下面这个方法的第一行代码便执行我们将要覆写的initialValue()方法,它会返回我们所需要的变量副本(可以看看上面的例子,在initialValue方法中我new了一个ArrayList对象并返回)。拿到我们的变量副本之后,如果当前线程的threadLocals变量不为空,则添加到其中,如果为空,需要创建一个ThreadLocalMap,也就是会执行setInitialValue方法中的createMap(t,value)方法。

/**     * 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;    }

下面方法的作用是把new一个ThreadLocalMap,并给当前线程也就是Thread对象的threadLocals变量赋值。这样之后,我们的变量副本便保存在了ThreadLocalMap中了,同时当前线程Thread的对象的threadLocals变量又保存了这个map,所以,我们的变量副本就和当前线程挂钩了。

/**     * Create the map associated with a ThreadLocal. Overridden in     * InheritableThreadLocal.     *     * @param t the current thread     * @param firstValue value for the initial entry of the map     * @param map the map to store.     */    void createMap(Thread t, T firstValue) {        t.threadLocals = new ThreadLocalMap(this, firstValue);    }
下面的是ThreadLocalMap静态内部类的构造方法。我们可以看到key为ThreadLocal类型,value是我们的变量副本。

/**         * Construct a new map initially containing (firstKey, firstValue).         * ThreadLocalMaps are constructed lazily, so we only create         * one when we have at least one entry to put in it.         */        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);        }

我们回到setInitialValue()方法,程序执行倒数第二行createMap方法后,接着就返回了value,这个value也就是我们覆写initialValue()所返回的值(也就是副本)。createMap()方法只是把我们的副本保存在一个map中,同时为当前线程的threadLocals变量赋值。

以上便是ThreadLocal类的主要原理,知道了上面的知识,对于public void set(T value)方法就很好理解了!

细节注意:

1、在TheadLocal类中定义了privatestatic AtomicInteger nextHashCode = newAtomicInteger();,它是静态的,也就是在ThreadLocal对象之间是共享的数据。

2ThreadLocal有一个静态的内部类ThreadLocalMap,它定义了privateEntry[]table;为什么是一个数组呢?一个线程可能有多个ThreadLocal对象,它们都保存在这个数组中了。怎么做到的呢?从ThreadLocal的源码我们可以看到,每次调用set方法的时候,都会先看看当前线程的threadLocals属性时候为空。如果不为空,就直接把我们的变量副本加到这个数组中。

转载地址:https://liangyihuai.blog.csdn.net/article/details/50755437 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:如何从KDE中切換到openSUSE的GNOME桌面环境
下一篇:nginx反向代理,负载均衡

发表评论

最新留言

做的很好,不错不错
[***.243.131.199]2024年05月02日 22时18分58秒