首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作甚密的其他几个类(内部类)
1.ThreadLocalMap
2.Thread
可能有人会觉得Thread与ThreadLocal有什么关系,其实真正的奥秘就在Thread类中的一行:
ThreadLocal.ThreadLocalMap threadLocals = null;
其中ThreadLocalMap的定义是在ThreadLocal类中,真正的引用却是在Thread类中
那么ThreadLocalMap究竟是什么呢?
可以看到这个类应该是一个Map,JDK的解释是
ThreadLocalMap is a customized hash map suitable only for maintaining thread local values
接下来的重点是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;
}
}
从中我们可以发现这个Map的key是ThreadLocal变量,value为用户的值,并不是网上大多数的列子key是线程的名字或者标识
到这里,我们就可以理解ThreadLocal究竟是如何工作的了
1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类
2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)
3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)
4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝
public class ThreadLocalSample {
public static void main(String[] args) {
ThreadTest test1 = new ThreadTest(10);
ThreadTest test2 = new ThreadTest(20);
test1.start();
test2.start();
}
}
class ThreadTest extends Thread {
public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();
public Integer num;
public ThreadTest(Integer num) {
super("线程" + num);
this.num = num;
}
@Override
public void run() {
System.out.println(this); //打印出当前运行的线程
System.out.println(thLocal); //打印出当前调用的 ThreadLocal
Integer n = thLocal.get(); //看ThreadLocal是否共享,如果其中一个能够打印出值表示共享
System.out.println("n:" + n);
if (n == null) {
thLocal.set(num);
}
System.out.println(thLocal.get());
}
}
接下来分析一下源码,就更加清楚了
ThreadLocal get方法源码
//threadlocal get方法源码
public T get() {
Thread t = Thread.currentThread();
//得到当前线程的ThreadLocalMap getMap(t) = t.threadLocals 参见下面getMap方法
ThreadLocalMap map = getMap(t);
if (map != null) {
//在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private T setInitialValue() {
/**
* 默认返回null,这个方法为protected可以继承
*/
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
/**
* 初次创建
*/
createMap(t, value);
return value;
}
通过上边的分析,我们发现,ThreadLocal类的使用虽然是用来解决多线程的问题的,但是还是有很明显的针对性
1.最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。典型的应用莫过于Spring,Hibernate等框架中对于多线程的处理了
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
这段代码,每个线程有自己的ThreadLocalMap,每个ThreadLocalMap中根据需要初始加载threadSession,这样的好处就是介于singleton与prototype之间,应用singleton无法解决线程,应用prototype开销又太大,有了ThreadLocal之后就好了,对于需要线程“霸占”的变量用ThreadLocal,而该类实例的方法均可以共享。
2.关于内存泄漏:
虽然ThreadLocalMap已经使用了weakReference,但是还是建议能够显示的使用remove方法。