Java源码解析ThreadLocal及使用场景
ThreadLocal是在多线程环境下经常使用的一个类。
这个类并不是为了解决多线程间共享变量的问题。举个例子,在一个电商系统中,用一个Long型变量表示某个商品的库存量,多个线程需要访问库存量进行销售,并减去销售数量,以更新库存量。在这个场景中,是不能使用ThreadLocal类的。
ThreadLocal适用的场景是,多个线程都需要使用一个变量,但这个变量的值不需要在各个线程间共享,各个线程都只使用自己的这个变量的值。这样的场景下,可以使用ThreadLocal。此外,我们使用ThreadLocal还能解决一个参数过多的问题。例如一个线程内的某个方法f1有10个参数,而f1调用f2时,f2又有10个参数,这么多的参数传递十分繁琐。那么,我们可以使用ThreadLocal来减少参数的传递,用ThreadLocal定义全局变量,各个线程需要参数时,去全局变量去取就可以了。
接下来我们看一下ThreadLocal的源码。首先是类的介绍。如下图。这个类提供了线程本地变量。这些变量使每个线程都有自己的一份拷贝。ThreadLocal期望能够管理一个线程的状态,例如用户id或事务id。例如下面的例子产生线程本地的唯一id。线程的id是第一次调用时进行复制,并且在后面的调用中保持不变。
Thisclassprovidesthread-localvariables. Thesevariablesdifferfromtheirnormalcounterpartsinthateachthreadthataccesses one(viaitsgetorsetmethod)hasitsown,independentlyinitializedcopyofthevariable.ThreadLocalinstancesaretypicallyprivatestaticfieldsinclassesthat wishtoassociatestatewithathread(e.g.,auserIDorTransactionID). Forexample,theclassbelowgeneratesuniqueidentifierslocaltoeachthread. Athread'sidisassignedthefirsttimeitinvokesThreadId.get()and remainsunchangedonsubsequentcalls. importjava.util.concurrent.atomic.AtomicInteger; publicclassThreadId{ //AtomicintegercontainingthenextthreadIDtobeassigned privatestaticfinalAtomicIntegernextId=newAtomicInteger(0); //Threadlocalvariablecontainingeachthread'sID privatestaticfinalThreadLocalthreadId= newThreadLocal (){ @OverrideprotectedIntegerinitialValue(){ returnnextId.getAndIncrement(); } }; //Returnsthecurrentthread'suniqueID,assigningitifnecessary publicstaticintget(){ returnthreadId.get(); } } Eachthreadholdsanimplicitreferencetoitscopyofathread-local variableaslongasthethreadisaliveandtheThreadLocalinstanceis accessible;afterathreadgoesaway,allofitscopiesofthread-local instancesaresubjecttogarbagecollection(unlessotherreferencesto thesecopiesexist).
下面看一下set方法。
set方法的作用是,把线程本地变量的当前线程的拷贝设置为指定的值。大部分子类无需重写该方法。首先获取当前线程,然后获取当前线程的ThreadLocalMap。如果ThreadLocalMap不为null,则设置当前线程的值为指定的值,否则调用createMap方法。
获取线程的ThreadLocalMap对象,是直接返回的线程的threadLocals,类型为ThreadLocalMap。也就是说,每个线程都有一个ThreadLocalMap对象,用于保存该线程关联的所有的ThreadLocal类型的变量。ThreadLocalMap的key是ThreadLocal,value是该ThreadLocal对应的值。具体什么意思呢?在程序中,我们可以定义不止一个ThreadLocal对象,一般会有多个,比如定义3个ThreadLocal
写到这里,自己回想起之前换工作面试时,面试官问自己关于ThreadLocal的实现原理。那个时候,为了准备面试,自己只在网上看了一些面试题,并没有真正掌握,在回答这个问题时,我有印象,自己回答的是用一个map,线程的id值作为key,变量值作为value,诶,露馅了啊。
/** *Setsthecurrentthread'scopyofthisthread-localvariable *tothespecifiedvalue.Mostsubclasseswillhavenoneedto *overridethismethod,relyingsolelyonthe{@link#initialValue} *methodtosetthevaluesofthread-locals. *@paramvaluethevaluetobestoredinthecurrentthread'scopyof *thisthread-local. **/ publicvoidset(Tvalue){ Threadt=Thread.currentThread(); ThreadLocalMapmap=getMap(t); if(map!=null) map.set(this,value); else createMap(t,value); } /** *GetthemapassociatedwithaThreadLocal.Overriddenin *InheritableThreadLocal. *@paramtthecurrentthread *@returnthemap **/ ThreadLocalMapgetMap(Threadt){ returnt.threadLocals; } /** *CreatethemapassociatedwithaThreadLocal.Overriddenin *InheritableThreadLocal. *@paramtthecurrentthread *@paramfirstValuevaluefortheinitialentryofthemap **/ voidcreateMap(Threadt,TfirstValue){ t.threadLocals=newThreadLocalMap(this,firstValue); }
接下来看一下get方法。
源码如下。首先获取当前线程的ThreadLocalMap,然后,从ThreadLocalMap获取该ThreadLocal变量对应的value,然后返回value。如果ThreadLocalMap为null,则说明该线程还没有设置该ThreadLocal变量的值,那么就返回setInitialValue方法的返回值。其中的initialValue方法的返回值,通常情况下为null。但是,子类可以重写initialValue方法以返回期望的值。
/** *Returnsthevalueinthecurrentthread'scopyofthis *thread-localvariable.Ifthevariablehasnovalueforthe *currentthread,itisfirstinitializedtothevaluereturned *byaninvocationofthe{@link#initialValue}method. *@returnthecurrentthread'svalueofthisthread-local **/ publicTget(){ Threadt=Thread.currentThread(); ThreadLocalMapmap=getMap(t); if(map!=null){ ThreadLocalMap.Entrye=map.getEntry(this); if(e!=null){ @SuppressWarnings("unchecked") Tresult=(T)e.value; returnresult; } } returnsetInitialValue(); } /** *Variantofset()toestablishinitialValue.Usedinstead *ofset()incaseuserhasoverriddentheset()method. *@returntheinitialvalue **/ privateTsetInitialValue(){ Tvalue=initialValue(); Threadt=Thread.currentThread(); ThreadLocalMapmap=getMap(t); if(map!=null) map.set(this,value); else createMap(t,value); returnvalue; } protectedTinitialValue(){ returnnull; }
文章的最后,简单介绍一下ThreadLocalMap这个类,该类是ThreadLocal的静态内部类。它对HashMap进行了改造,用于保存各个ThreadLocal变量和某线程的该变量的值的映射关系。每个线程都有一个ThreadLocalMap类型的属性。ThreadLocalMap中的table数组的长度,与该线程访问的ThreadLocal类型变量的个数有关,而与别的无关。
Thisistheend。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。如果你想了解更多相关内容请查看下面相关链接