您的位置 首页 > 德语词汇

TL是什么意思?用法、例句?从TL、ITL到TTL

很多朋友对于TL是什么意思?用法、例句和从TL、ITL到TTL不太懂,今天就由小编来为大家分享,希望可以帮助到大家,下面一起来看看吧!

TL是什么意思?用法、例句?从TL、ITL到TTL

ThreadLocal(TL)是Java中一种线程局部变量实现机制,他为每个线程提供一个单独的变量副本,保证多线程场景下,变量的线程安全。经常用于代替参数的显式传递。

InheritableThreadLocal(ITL)是JDK提供的TL增强版,而TransmittableThreadLocal(TTL)是阿里开源的ITL增强版

这些ThreadLocal在不同场景下有不同用途,我们来分析一下:

ThreadLocal主要的方法有四个:initialValue、set、get、remove

当线程首次访问该ThreadLocal时(ThreadLocal.get()),会进行初始化赋值。我们常用两种方法初始化ThreadLocal

ThreadLocal<String>threadLocal=newThreadLocal<String>(){\n@Override\nprotectedStringinitialValue(){\nreturn"";\n}\n};\n\n2.1.2、调用ThreadLocal.withInitial

ThreadLocal<String>threadLocal=ThreadLocal.withInitial(()->"");\n\n

他会创建一个SuppliedThreadLocal内部类

publicstatic<S>ThreadLocal<S>withInitial(Supplier<?extendsS>supplier){\nreturnnewSuppliedThreadLocal<>(supplier);\n}\n\n

该类重写了initialValue方法

staticfinalclassSuppliedThreadLocal<T>extendsThreadLocal<T>{\n\nprivatefinalSupplier<?extendsT>supplier;\n\nSuppliedThreadLocal(Supplier<?extendsT>supplier){\nthis.supplier=Objects.requireNonNull(supplier);\n}\n\n@Override\nprotectedTinitialValue(){\n//当该线程首次访问ThreadLocal时,会间接调用lambda表达式初始化\nreturnsupplier.get();\n}\n}\n\n

??ITL并没有重新实现withInitial,如果使用withInitial则会创建STL,失去自己增强的特性

publicvoidset(Tvalue){\nThreadt=Thread.currentThread();\nThreadLocalMapmap=getMap(t);\nif(map!=null)\nmap.set(this,value);\nelse\ncreateMap(t,value);\n}\n\n

这里出现了一个关键属性ThreadLocalMap,类定义在ThreadLocal中,是Thread的成员变量

ThreadLocalMapgetMap(Threadt){\nreturnt.threadLocals;\n}\n\n

ThreadLocalMap内部还有一个内部类Entry,是存值的地方

staticclassThreadLocalMap{\nstaticclassEntryextendsWeakReference<ThreadLocal<?>>{\nObjectvalue;\nEntry(ThreadLocal<?>k,Objectv){\n//ThreadLocal的引用是“key”\nsuper(k);\n//线程局部变量是value\nvalue=v;\n}\n}\n//Entry数组\n//value具体放在哪个index下,是由ThreadLocal的hashCode算出来的\nprivateEntry[]table;\n}\n\n2.3、取值——get

publicTget(){\nThreadt=Thread.currentThread();\n//1、获取线程的ThreadLocalMap\nThreadLocalMapmap=getMap(t);\nif(map!=null){\n//2、根据ThreadLocal的hashCode,获取对应Entry下的value\nThreadLocalMap.Entrye=map.getEntry(this);\nif(e!=null){\n@SuppressWarnings("unchecked")\nTresult=(T)e.value;\nreturnresult;\n}\n}\n//3、如果没有赋过值,则初始化\nreturnsetInitialValue();\n}\n\n2.4、清空——remove

publicvoidremove(){\nThreadLocalMapm=getMap(Thread.currentThread());\nif(m!=null)\n//会将对应Entry、包括他的key、value手动置null\nm.remove(this);\n}\n\n3、InheritableThreadLocal3.1、TL在父子线程场景下存在的问题

我们先来看一个例子

publicstaticvoidmain(String[]args)throwsInterruptedException{\nThreadLocal<String>threadLocal=ThreadLocal.withInitial(()->"A");\nthreadLocal.set("B");\nThreadthread=newThread(()->{\nSystem.out.println("子线程ThreadLocal:"+threadLocal.get());\n},"子线程");\nthread.start();\nthread.join();\n}\n\n

打印结果如下,可见子线程的ThreadLocal是初始值,并没有使用父线程修改后的值:

子线程ThreadLocal:A\n\n

线程的ThreadLocalMap是首次访问时创建的,所以子线程使用ThreadLocal的时候,会初始化一个新的ThreadLocal,线程局部变量为默认值

??所以,TL不具有遗传性

为了解决TL子线程遗传性的问题,JDK引入了ITL

他继承ThreadLocal,重写了childValue、getMap、createMap三个方法

publicclassInheritableThreadLocal<T>extendsThreadLocal<T>{\n\nprotectedTchildValue(TparentValue){\nreturnparentValue;\n}\n\nThreadLocalMapgetMap(Threadt){\nreturnt.inheritableThreadLocals;\n}\n\nvoidcreateMap(Threadt,TfirstValue){\nt.inheritableThreadLocals=newThreadLocalMap(this,firstValue);\n}\n}\n\n

这里出现了inheritableThreadLocals,他存储的就是从父线程拷贝过来的ThreadLocal,这个值是在父线程首次修改ThreadLocal的时候赋值的,然后在子线程创建时拷贝过来的

//父线程部分:\npublicvoidset(Tvalue){\nThreadt=Thread.currentThread();\n//该方法被ITL重写,访问inheritableThreadLocals为null\nThreadLocalMapmap=getMap(t);\nif(map!=null)\nmap.set(this,value);\nelse\n//该方法同样被ITL重写,创建一个ThreadLocalMap赋值给inheritableThreadLocals\ncreateMap(t,value);\n}\n\n//子线程部分:\npublicThread(Runnabletarget){\ninit(null,target,"Thread-"+nextThreadNum(),0);\n}\n\nprivatevoidinit(ThreadGroupg,Runnabletarget,Stringname,\nlongstackSize,AccessControlContextacc,\nbooleaninheritThreadLocals){\n//省略一些代码...\n\n//获取当前线程(父线程、也就是创建子线程的线程)\nThreadparent=currentThread();\n//1、允许ThreadLocal遗传(这个默认为true)\n//2、inheritableThreadLocals不为空,因为父线程调用set了\n//父线程不调用set,那ThreadLocal就是初始值,那直接初始化就好了,也不用进该分支\nif(inheritThreadLocals&&parent.inheritableThreadLocals!=null)\nthis.inheritableThreadLocals=\nThreadLocal.createInheritedMap(parent.inheritableThreadLocals);\n}\n\n//createInheritedMap使用该构造函数,根据父线程的inheritableThreadLocals进行深拷贝\nprivateThreadLocalMap(ThreadLocalMapparentMap){\nEntry[]parentTable=parentMap.table;\nintlen=parentTable.length;\nsetThreshold(len);\ntable=newEntry[len];\n//深拷贝父线程ThreadLocalMap\nfor(intj=0;j<len;j++){\nEntrye=parentTable[j];\nif(e!=null){\n@SuppressWarnings("unchecked")\nThreadLocal<Object>key=(ThreadLocal<Object>)e.get();\nif(key!=null){\n//childValue被ITL重写,返回父线程ThreadLocal的值\nObjectvalue=key.childValue(e.value);\nEntryc=newEntry(key,value);\ninth=key.threadLocalHashCode&(len-1);\nwhile(table[h]!=null)\nh=nextIndex(h,len);\ntable[h]=c;\nsize++;\n}\n}\n}\n}\n\n

使用ITL的效果

publicstaticvoidmain(String[]args)throwsInterruptedException{\nThreadLocal<String>threadLocal=newInheritableThreadLocal<String>(){\n@Override\nprotectedStringinitialValue(){\nreturn"A";\n}\n};\nthreadLocal.set("B");\nThreadthread=newThread(()->{\nSystem.out.println("子线程ThreadLocal:"+threadLocal.get());\n},"子线程");\nthread.start();\n\nthread.join();\n}\n\n

打印结果如下,子线程拷贝了父线程ThreadLocal:

子线程ThreadLocal:B

总结一下,ITL解决父子线程遗传性的核心思路是,将可遗传的ThreadLocal放在父线程新的ThreadLocalMap中,在子线程首次使用时进行拷贝

publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{\nThreadLocal<String>threadLocal=newInheritableThreadLocal<String>(){\n@Override\nprotectedStringinitialValue(){\nreturn"A";\n}\n};\nthreadLocal.set("B");\nExecutorServiceexecutorService=Executors.newFixedThreadPool(1);\n//1、子线程第一次获取ThreadLocal\nexecutorService.submit(()->System.out.println("子线程ThreadLocal:"+threadLocal.get())).get();\nThread.sleep(1000);\n//2、父线程修改ThreadLocal\nthreadLocal.set("C");\nSystem.out.println("父线程修改ThreadLocal为"+threadLocal.get());\n//3、子线程第二次获取ThreadLocal\nexecutorService.submit(()->System.out.println("子线程ThreadLocal:"+threadLocal.get())).get();\n}\n\n

打印结果如下,子线程在第二次打印时,并没有拷贝父线程的ThreadLocal,使用的还是首次拷贝的值:

子线程ThreadLocal:B\n父线程修改ThreadLocal为C\n子线程ThreadLocal:B

??可复用的子线程不会感知父线程ThreadLocal的变化

TTL在ITL上做了稍微复杂的封装,我们从使用开始了解

<dependency>\n<groupId>com.alibaba</groupId>\n<artifactId>transmittable-thread-local</artifactId>\n<version>latest</version>\n</dependency>\n\n

在使用TTL时,线程需要经过TTL封装,线程池同理

publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{\nThreadLocal<String>threadLocal=newTransmittableThreadLocal<String>(){\n@Override\nprotectedStringinitialValue(){\nreturn"A";\n}\n};\nthreadLocal.set("B");\nExecutorServiceexecutorService=TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(1));\nexecutorService.submit(()->System.out.println("子线程ThreadLocal:"+threadLocal.get())).get();\nThread.sleep(1000);\nthreadLocal.set("C");\nSystem.out.println("父线程修改ThreadLocal为"+threadLocal.get());\nexecutorService.submit(()->System.out.println("子线程ThreadLocal:"+threadLocal.get())).get();\nThread.sleep(1000);\nexecutorService.submit(()->{\nthreadLocal.set("D");\nSystem.out.println("子线程修改ThreadLocal为"+threadLocal.get());\n});\nThread.sleep(1000);\nexecutorService.submit(()->System.out.println("子线程ThreadLocal:"+threadLocal.get()));\nThread.sleep(1000);\n}\n\n

打印结果如下,子线程每次都会获取父线程的ThreadLocal

子线程ThreadLocal:B\n父线程修改ThreadLocal为C\n子线程ThreadLocal:C\n子线程修改ThreadLocal为D\n子线程ThreadLocal:C

从使用上看,TTL要求将任务封装,那我们就从ThreadLocal和ExecutorService两部分入手

下面是TTL的取值和赋值逻辑,都涉及一个关键方法addThisToHolder,对应的属性holder会在线程池执行任务时用到

//TransmittableThreadLocal.addThisToHolder()\nprivatevoidaddThisToHolder(){\n//InheritableThreadLocal<WeakHashMap<TransmittableThreadLocal<Object>,?>>holder\nif(!holder.get().containsKey(this)){\n//holder是静态变量,他会把TTL存到当前线程的map中\n//value是null,他其实是把Map当Set用\n//主线程赋值时,会获取主线程的holderMap,然后把TTL存进去\nholder.get().put((TransmittableThreadLocal<Object>)this,null);\n}\n}\n\n@Override\npublicfinalvoidset(Tvalue){\nif(!disableIgnoreNullValueSemantics&&null==value){\nremove();\n}else{\nsuper.set(value);\n//当主线程赋值时,会将自己的TTL放到自己的map中\naddThisToHolder();\n}\n}\n\n@Override\npublicfinalTget(){\nTvalue=super.get();\nif(disableIgnoreNullValueSemantics||null!=value)\naddThisToHolder();\nreturnvalue;\n}\n\n4.2.3、TTL对任务的封装

//我们通过TtlExecutors.getTtlExecutorService()对线程池进行封装\npublicstaticExecutorServicegetTtlExecutorService(@NullableExecutorServiceexecutorService){\nif(TtlAgent.isTtlAgentLoaded()||executorService==null||executorServiceinstanceofTtlEnhanced){\nreturnexecutorService;\n}\n//入参是线程池,通过包装类代理线程池的操作\nreturnnewExecutorServiceTtlWrapper(executorService);\n}\n\n//ExecutorServiceTtlWrapper.submit()\npublicFuture<?>submit(@NonNullRunnabletask){\n//将提交的任务进行封装\nreturnexecutorService.submit(TtlRunnable.get(task));\n}\n\n4.2.3.1、任务构建

TtlRunnable构造方法

这里都是主线程在操作,因为任务是主线程提交的

privateTtlRunnable(@NonNullRunnablerunnable,booleanreleaseTtlValueReferenceAfterRun){\nthis.capturedRef=newAtomicReference<Object>(capture());\nthis.runnable=runnable;\nthis.releaseTtlValueReferenceAfterRun=releaseTtlValueReferenceAfterRun;\n}\n\n

这里有一个关键属性capturedRef,他是一个原子引用,存了TTL

//TrasmitableThreadLocal.Transmitter\npublicstaticObjectcapture(){\n//获取ttl的值构建快照\nreturnnewSnapshot(captureTtlValues(),captureThreadLocalValues());\n}\n\nprivatestaticHashMap<TransmittableThreadLocal<Object>,Object>captureTtlValues(){\nHashMap<TransmittableThreadLocal<Object>,Object>ttl2Value=newHashMap<TransmittableThreadLocal<Object>,Object>();\nfor(TransmittableThreadLocal<Object>threadLocal:holder.get().keySet()){\n//将主线程TTL的值存到当前任务中\nttl2Value.put(threadLocal,threadLocal.copyValue());\n}\nreturnttl2Value;\n}\n\n4.2.3.2、任务执行

任务执行的代码如下,在任务执行前回放ThreadLocal,在任务执行后恢复ThreadLocal:

这里都是子线程在操作,因为任务都是子线程执行的

@Override\npublicvoidrun(){\nObjectcaptured=capturedRef.get();\nif(captured==null||releaseTtlValueReferenceAfterRun&&!capturedRef.compareAndSet(captured,null)){\nthrownewIllegalStateException("TTLvaluereferenceisreleasedafterrun!");\n}\n//1、备份子线程ThreadLocal\n//2、使用主线程提交任务时构建的ThreadLocal副本,将子线程ThreadLocal覆盖\nObjectbackup=replay(captured);\ntry{\n//3、任务执行\nrunnable.run();\n}finally{\n//3、使用之前备份的子线程ThreadLocal进行恢复\nrestore(backup);\n}\n}\n\n

总结一下,TTL让子线程感知父线程变化的核心思路是,主线程在任务提交时构建ThreadLocal副本,在子线程执行任务时供其使用

??提交和执行任务会对TTL进行若干操作,理论上对性能有一点点影响,官方性能测试结论说损耗可忽略

来源:京东云开发者自猿其说Tech

关于TL是什么意思?用法、例句到此分享完毕,希望能帮助到您。

本站涵盖的内容、图片、视频等数据,部分未能与原作者取得联系。若涉及版权问题,请及时通知我们并提供相关证明材料,我们将及时予以删除!谢谢大家的理解与支持!

Copyright © 2023