首页 > 代码库 > 浅谈ThreadLocal

浅谈ThreadLocal

      ThreadLocal这个名臣带有一定的迷惑性,千万不要认为ThreadLocal是线程的一种实现,网上很多人认为它应该叫ThreadLocalVariable更贴切,我对此也非常赞同。ThreadLocal存在的意义就是为了解决线程之间数据数据的冲突,ThreadLocal是线程的局部变量,它里面的数据只存活在线程的声明周期之中,而且必须是当前线程才能获取到对应的数据,其他的线程不能获取到当前线程的ThreadLocal中的数据。

       在对ThreadLocal做了一个简单的介绍后,下面开始逐一分析。

1.ThreadLocal是什么?

    ThreadLocal的本质是线程局部变量,可以理解为ThreadLocalVariable,它并非是线程的本地实现,不是一个Thread。

    ThreadLocal的源码中类声明如下:

public class ThreadLocal<T> {...

2.ThreadLocal的作用?

       ThreadLocal存在是为了解决什么问题的呢?在多线程并发的情况下,各个线程的在其上下文环境下保存一些变量副本,

可以独立的改变自己的副本,而不会和其他线程的副本冲突。

       ThreadLocal的数据实际存储在ThreadLocalMap中,并且以当前的ThreadLocal对象作为key,当对ThreadLocal做get和set方法的时候,都是以this为key进行操作的,因此其的ThreadLocal对象获取当前的ThreadLocal对象的值。

    set

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

get

  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();
    }


3.ThreadLocal中的变量存储在何处?

        ThreadLocal中数据式存储在ThreadLocal.ThreadLocalMap中的。这个ThreadLocalMap的对象是被Thread对象的threadLocals的属性引用的。由此可见,ThreadLocal中变量存在Thread对象中,因此在线程的存活期间,如果没有刻意的去删除这些属性,实际上在线程声明周期中均可以对这些变量进行访问和操作。

        ThreadLocalMap是作为Thread类的一个字段。

class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private char	name[];
    private int         priority;
    private Thread	threadQ;
    private long	eetop;

    /* Whether or not to single_step this thread. */
    private boolean	single_step;

    /* Whether or not the thread is a daemon thread. */
    private boolean	daemon = false;

    /* JVM state */
    private boolean	stillborn = false;

    /* What will be run. */
    private Runnable target;

    /* The group of this thread */
    private ThreadGroup	group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
	return threadInitNumber++;
    }

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
     //请看这里----
    ThreadLocal.ThreadLocalMap threadLocals = null;

         ThreadLocalMap何时创建,并将对象引用和Thread的threadLocals属性关联上呢?在ThreadLocal中有一个createMap的方法,在这个方法里会创建一个ThreadLocalMap。请看下面代码:

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

         此时还没有回答何时回创建ThreadLocalMap这个问题。实际上在前面的set方法中已经看到调用createMap的方法了,说明在第一设置数据的时候,如果使用当前ThreadLocal对象没有找到ThreadLocalMap对象的时候,那么将会去创建ThreadLocalMap对象,但是起初没有set就开始调用get方法呢?在get方法里面调用一个叫setInitialValue的方法。事实上setInitialValue和set方法非常相似,在setInitialValue中也调用createMap方法。请看下面代码:

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

  至此,我可以知道了ThreadLocalMap是存储在Thread对象中,其次TheadLocal会在首次调用get或者set的时候创建ThreadLocalMap对象,并且将这个ThreadLocalMap的对象赋给当前线程的threadlocals属性。

4.ThreadLocal中数据存储详细分析。

    (1)ThreadLocal中数据是存储在ThreadLocalMap的对象中。并且是ThreadLocalMap对象和Thread对象时一对一的关系。

     (2)一个ThreadLocalMap对象对应的ThreadLocal对象是N个。

      (3)ThreadLocalMap的Entry是一个WeakReference<ThreadLocal>子类,ThreadLocalMap中存储的时候是以ThreadLocal的对象作为key,当key释放了引用(key==null),ThreadLocalMap会立刻移除对应Entry。

        我们再次看一下ThreadLocalMap.Entry的声明:

 static class Entry extends WeakReference<ThreadLocal> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }

5.ThreadLocal回收。参考资料来自ThreadLocal内存泄露

被废弃了的ThreadLocal所绑定对象的引用,会在以下4情况被清理。

如果此时外部没有绑定对象的引用,则该绑定对象就能被回收了:

1 Thread结束时。

2 当Thread的ThreadLocalMap的threshold超过最大值时。

3 向Thread的ThreadLocalMap中存放一个ThreadLocal,hash算法没有命中既有Entry,而需要新建一个Entry时。

4 手工通过ThreadLocal的remove()方法或set(null)。

因此如果我们粗暴的把ThreadLocal设置null,而不调用remove()方法或set(null),那么就可能造成ThreadLocal绑定的对象长期也能被回收,因而产出内存泄露。


浅谈ThreadLocal