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。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对毛票票的支持。如果你想了解更多相关内容请查看下面相关链接