原文地址:http://www.iteye.com/topic/777716
ThreadLocal<T>类在Spring,Hibernate等框架中起到了很大的作用,对于其工作原理,很多网上的文章分析的不够彻底,甚至有些误解。
首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作甚密的其他几个类(内部类)
1.ThreadLocalMap
2.Thread
可能有人会觉得Thread与ThreadLocal有什么关系,其实真正的奥秘就在Thread类中的一行:
- ThreadLocal.ThreadLocalMapthreadLocals=null;
其中ThreadLocalMap的定义是在ThreadLocal类中,真正的引用却是在Thread类中
那么ThreadLocalMap究竟是什么呢?
可以看到这个类应该是一个Map,JDK的解释是
接下来的重点是ThreadLocalMap中用于存储数据的entry
- staticclassEntryextendsWeakReference<ThreadLocal>{
- /**ThevalueassociatedwiththisThreadLocal.*/
- Objectvalue;
- Entry(ThreadLocalk,Objectv){
- super(k);
- value=v;
- }
- }
从中我们可以发现这个Map的key是ThreadLocal变量,value为用户的值,并不是网上大多数的列子key是线程的名字或者标识
到这里,我们就可以理解ThreadLocal究竟是如何工作的了
1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类
2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)
3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)
4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝
- /**
- *@authormxdba
- *
- */
- publicclassThreadLocalSample{
- publicstaticvoidmain(String[]args){
- ThreadTesttest1=newThreadTest(10);
- ThreadTesttest2=newThreadTest(20);
- test1.start();
- test2.start();
- }
- }
- /**
- *此线程有两个ThreadLocal变量,但是由于ThreadLocal是延迟初始的,
- *所以在debug时可以看到线程名为“线程20”的线程的ThreadLocalMap中没有thLcal2这个entry
- *@authormxdba
- *
- */
- classThreadTestextendsThread{
- publicstaticThreadLocal<Integer>thLocal=newThreadLocal<Integer>();
- publicstaticThreadLocal<String>thLocal2=newThreadLocal<String>();
- publicIntegernum;
- publicThreadTest(Integernum){
- super("线程"+num);
- this.num=num;
- }
- @Override
- publicvoidrun(){
- Integern=thLocal.get();
- if(num!=20){
- Strings=thLocal2.get();
- }
- if(n==null){
- thLocal.set(num);
- }
- System.out.println(thLocal.get());
- }
- }
接下来分析一下源码,就更加清楚了
- /**
- *关键方法,返回当前Thread的ThreadLocalMap
- *[[[每个Thread返回各自的ThreadLocalMap,所以各个线程中的ThreadLocal均为独立的]]]
- */
- ThreadLocalMapgetMap(Threadt){
- returnt.threadLocals;
- }
- publicTget(){
- Threadt=Thread.currentThread();
- /**
- *得到当前线程的ThreadLocalMap
- */
- ThreadLocalMapmap=getMap(t);
- if(map!=null){
- /**
- *在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry
- */
- ThreadLocalMap.Entrye=map.getEntry(this);
- if(e!=null)
- return(T)e.value;
- }
- returnsetInitialValue();
- }
- privateTsetInitialValue(){
- /**
- *默认返回null,这个方法为protected可以继承
- */
- Tvalue=initialValue();
- Threadt=Thread.currentThread();
- ThreadLocalMapmap=getMap(t);
- if(map!=null)
- map.set(this,value);
- else
- /**
- *初次创建
- */
- createMap(t,value);
- returnvalue;
- }
- /**
- *给当前thread初始ThreadlocalMap
- */
- voidcreateMap(Threadt,TfirstValue){
- t.threadLocals=newThreadLocalMap(this,firstValue);
- }
通过上边的分析,我们发现,ThreadLocal类的使用虽然是用来解决多线程的问题的,但是还是有很明显的针对性
1.最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。典型的应用莫过于Spring,Hibernate等框架中对于多线程的处理了
- privatestaticfinalThreadLocalthreadSession=newThreadLocal();
- publicstaticSessiongetSession()throwsInfrastructureException{
- Sessions=(Session)threadSession.get();
- try{
- if(s==null){
- s=getSessionFactory().openSession();
- threadSession.set(s);
- }
- }catch(HibernateExceptionex){
- thrownewInfrastructureException(ex);
- }
- returns;
- }
这段代码,每个线程有自己的ThreadLocalMap,每个ThreadLocalMap中根据需要初始加载threadSession,这样的好处就是介于singleton与prototype之间,应用singleton无法解决线程,应用prototype开销又太大,有了ThreadLocal之后就好了,对于需要线程“霸占”的变量用ThreadLocal,而该类实例的方法均可以共享。
2.关于内存泄漏:
虽然ThreadLocalMap已经使用了weakReference,但是还是建议能够显示的使用remove方法。