首页>>后端>>java->InheritableThreadLocal可继承的线程本地变量的源码解析

InheritableThreadLocal可继承的线程本地变量的源码解析

时间:2023-12-01 本站 点击:0

1 从ThreadLocal说起

在前面ThreadLocal文章中:ThreadLocal的源码解析以及内存泄漏的原理分析。我们知道每个线程都具有一个threadLocals属性,用于存放线程本地变量。

当然如果我们眼睛够细,在Thread类的threadLocals属性下面还可以发现一个同类型的属性inheritableThreadLocals,那这是变量又是干什么的呢?

/*ThreadLocalvaluespertainingtothisthread.Thismapismaintained*bytheThreadLocalclass.*/ThreadLocal.ThreadLocalMapthreadLocals=null;/**InheritableThreadLocalvaluespertainingtothisthread.Thismapis*maintainedbytheInheritableThreadLocalclass.*/ThreadLocal.ThreadLocalMapinheritableThreadLocals=null;

实际上,该属性用于实现线程本地变量的继承性:子线程创建时可以从父线程继承线程本地变量。inheritableThreadLocals主要存储可自动向子线程中传递的ThreadLocalMap,他也对应着一个类:InheritableThreadLocal。

在hreadLocal的文章的第一个案例的中,同一个ThreadLocal 变量在父线程(main)中被设置值后, 在子线程(child)中是获取不到的。这应该是正常现象,因为在子线程thread 里面调用get 方法时当前线程为thread 线程,而这里调用set方法设置线程变量的是main 线程,两者是不同的线程,自然子线程访问时返回null。

但是可能有些业务,需要子线程获取到父线程的数据,那么有没有办法让子线程能访问到父线程中的值? 答案是有,那就是InheritableThreadLocal。

2 InheritableThreadLocal的原理

2.1 InheritableThreadLocal的源码

public class InheritableThreadLocal< T > extends ThreadLocal< T >

InheritableThreadLocal继承自ThreadLocal , 保存线程本地变量的集合也是使用的ThreadLocalMap。但是其增加了一个特性,就是让子线程可以访问在父线程中设置的本地变量。

InheritableThreadLocal类的源码很少,重写了ThreadLocal的3个函数,也是该类的源全部源码:

/***1*InheritableThreadLocal重写的实现,当调用get/set方法获取当前线程内部的map变量时,获取*的是inheritableThreadLocals而不再是threadLocals。*/ThreadLocalMapgetMap(Threadt){returnt.inheritableThreadLocals;}/***ThreadLocal的实现*/ThreadLocalMapgetMap(Threadt){returnt.threadLocals;}/***操作InheritableThreadLocal对象时,第一次调用set/get方法时*创建的是当前线程的inheritableThreadLocals变量的实例而不再是threadLocals变量实例。*/voidcreateMap(Threadt,TfirstValue){t.inheritableThreadLocals=newThreadLocalMap(this,firstValue);}/***ThreadLocal的实现*/voidcreateMap(Threadt,TfirstValue){t.threadLocals=newThreadLocalMap(this,firstValue);}/***3*该函数在父线程中创建子线程并向子线程复制inheritableThreadLocal变量时使用。*在ThreadLocal中实现为抛出UnsupportedOperationException异常*/protectedTchildValue(TparentValue){returnparentValue;}/***ThreadLocal的实现*/TchildValue(TparentValue){thrownewUnsupportedOperationException();}

2.2 传递数据的原理

传递数据的原理还要从创建线程开始说起!实际上子线程的很多属性如果自己没有指定的话,都会继承父线程的属性,比如线程组、守护/用户线程属性、线程的上下文对象等。

/***Thread类的构造器**@paramtarget线程任务*@paramname线程名字*/publicThread(Runnabletarget,Stringname){//内部调用init方法init(null,target,name,0);}/***Thread类的init方法,用于初始化线程的参数**@paramg线程组*@paramtarget线程任务*@paramname线程名字*@paramstackSize当前线程栈大小*/privatevoidinit(ThreadGroupg,Runnabletarget,Stringname,longstackSize){//调用另外一个init方法init(g,target,name,stackSize,null,true);}/***Thread类的另一个init方法**@paramg线程组*@paramtarget线程任务*@paramname线程名字*@paramstackSize当前线程栈大小*@paramacc访问控制权限*@paraminheritThreadLocals如果为true,则从构造线程(父线程)中继承线程局部变量的初始值*/privatevoidinit(ThreadGroupg,Runnabletarget,Stringname,longstackSize,AccessControlContextacc,booleaninheritThreadLocals){//设置名字this.name=name;//获取当前线程,即父线程(main)Threadparent=currentThread();//如果inheritThreadLocals为true并且父线程的inheritableThreadLocals变量不为nullif(inheritThreadLocals&&parent.inheritableThreadLocals!=null)//4则将父线程inheritableThreadLocals的数据传递至子线程的inheritableThreadLocals属性中//注意这里时传递的引用值,对于引用类型的值(除了string)相当于浅拷贝,实际上他们还是同一个对象this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);//…………}/***4*ThreadLocal类的方法*父线程的inheritableThreadLocals包装到子线程的inheritableThreadLocals中**@paramparentMap父线程的inheritableThreadLocals*@returnThreadLocalMap*/staticThreadLocalMapcreateInheritedMap(ThreadLocalMapparentMap){//5新建ThreadLocalMap,包含指定的ThreadLocalMap的所有数据returnnewThreadLocalMap(parentMap);}/***ThreadLocalMap类的构造函数,包含指定的ThreadLocalMap的所有数据**@paramparentMap指定的ThreadLocalMap对象*/privateThreadLocalMap(ThreadLocalMapparentMap){//获取table数组ThreadLocalMap.Entry[]parentTable=parentMap.table;//获取长度intlen=parentTable.length;//设置扩容阀值setThreshold(len);//新建tabletable=newThreadLocalMap.Entry[len];for(intj=0;j<len;j++){ThreadLocalMap.Entrye=parentTable[j];if(e!=null){@SuppressWarnings("unchecked")ThreadLocal<Object>key=(ThreadLocal<Object>)e.get();if(key!=null){//这里终于调用到了重写的childValue方法//用于把父线程的inheritableThreadLocals属性的数据复制到新的ThreadLocalMap对象中Objectvalue=key.childValue(e.value);ThreadLocalMap.Entryc=newThreadLocalMap.Entry(key,value);inth=key.threadLocalHashCode&(len-1);while(table[h]!=null)h=nextIndex(h,len);table[h]=c;size++;}}}}

3 总结和案例

InheritableThreadLocal 类重写了三个方法:

createMap将创建的ThreadLocalMap赋值给具体线程的inheritableThreadLocals变量。

getMap将返回具体线程的inheritableThreadLocals变量,从而让本地变量保存到了inheritableThreadLocals变量里面,那么线程在通过InheritableThreadLocal 类实例的set 或者get 方法设置变量时,就会创建当前线程的inheritableThreadLocals 变量。

当在父线程中创建子线程时,构造函数会把父线程中inheritableThreadLocals变量里面的本地变量复制一份(浅克隆)保存到子线程的inheritableThreadLocals 变量里面。

InheritableThreadLocal传递数据的案例:

publicclassInheritableThreadLocalTest{staticInheritableThreadLocal<InheritableThreadLocalTest>threadLocal=newInheritableThreadLocal<>();publicstaticvoidset(){threadLocal.set(newInheritableThreadLocalTest());}publicstaticInheritableThreadLocalTestget(){returnthreadLocal.get();}publicstaticvoidmain(String[]args)throwsInterruptedException{System.out.println("主线程中尝试获取值====>"+get());System.out.println("主线程中设置值====>");set();//主线程中尝试获取值System.out.println("主线程中再次尝试获取值====>"+get());System.out.println();System.out.println("开启一条子线程====>");Threadthread=newThread(newTh1(),"child");thread.start();//主线程等待子线程执行完毕thread.join();System.out.println();System.out.println("等待子线程执行完毕,主线程中再次尝试获取值====>"+get());System.out.println("获取到的还是父线程原来的值,即父可以传递数据给子,子不能传递数据给父");}staticclassTh1implementsRunnable{@Overridepublicvoidrun(){System.out.println("子线程中尝试获取值====>"+get());System.out.println("能够直接获取到,说明父线程的数据传递给了子线程,并且获取到的对象和父类获取到的对象还是同一个,即浅拷贝");System.out.println("子线程中设置值====>");set();System.out.println("子线程中再次尝试获取值====>"+get());System.out.println("此时获取到的,是自己设置的值");System.out.println();}}@OverridepublicStringtoString(){returnthis.hashCode()+"";}}

结果:

主线程中尝试获取值====>null主线程中设置值====>主线程中再次尝试获取值====>1878246837开启一条子线程====>子线程中尝试获取值====>1878246837能够直接获取到,说明父线程的数据传递给了子线程,并且获取到的对象和父类获取到的对象还是同一个,即浅拷贝子线程中设置值====>子线程中再次尝试获取值====>185894025此时获取到的,是自己设置的值等待子线程执行完毕,主线程中再次尝试获取值====>1878246837获取到的还是父线程原来的值,即父可以传递数据给子,子不能传递数据给父

可以看出,子线程可以正常获取到父线程本地变量的值。

InheritableThreadLocal常常用在调用链路追踪上。

如果有什么不懂或者需要交流,可以留言。另外希望点赞、收藏、关注,我将不间断更新各种Java学习博客!


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/java/5526.html