ThreadLocal这个名臣带有必定的迷惑性,千万不要认为ThreadLocal是线程的一种实现,网上不少人认为它应该叫ThreadLocalVariable更贴切,我对此也很是赞同。ThreadLocal存在的意义就是为了解决线程之间数据数据的冲突,ThreadLocal是线程的局部变量,它里面的数据只存活在线程的声明周期之中,并且必须是当前线程才能获取到对应的数据,其余的线程不能获取到当前线程的ThreadLocal中的数据。html
在对ThreadLocal作了一个简单的介绍后,下面开始逐一分析。java
1.ThreadLocal是什么?算法
ThreadLocal的本质是线程局部变量,能够理解为ThreadLocalVariable,它并不是是线程的本地实现,不是一个Thread。多线程
ThreadLocal的源码中类声明以下:并发
public class ThreadLocal<T> {...
2.ThreadLocal的做用?this
ThreadLocal存在是为了解决什么问题的呢?在多线程并发的状况下,各个线程的在其上下文环境下保存一些变量副本,spa
能够独立的改变本身的副本,而不会和其余线程的副本冲突。线程
ThreadLocal的数据实际存储在ThreadLocalMap中,而且以当前的ThreadLocal对象做为key,当对ThreadLocal作get和set方法的时候,都是以this为key进行操做的,所以其的ThreadLocal对象获取当前的ThreadLocal对象的值。code
setorm
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绑定的对象长期也能被回收,于是产出内存泄露。