凡事有两面性,知道其优点和劣势才是咱们选择使用与否的判断依据编程
一、优点 tomcat
A:ThreadLocal 可使得线程独有的局部变量,在整个线程存活期间内跨越类和实例的进行使用,等同于为线程内多个实例节点提供了数据bus安全
B:ThreadLocal 存储的是线程副本,线程消亡后,其内存留的副本数据会随着gc消亡并发
C:ThreadLocal 从某种角度上来看[线程并发的时候],是牺牲空间来获取时间的一种操做函数
D:能自然解决线程安全问题,由于是依据线程副本进行的保存,因此其保存的局部变量不会被其余线程获取编码
二、劣势spa
A:ThreadLocal 占用了内存空间,由于Threadl为每一个线程都建立了副本线程
B:使用不当会致使内存泄露,尤为是对没有良好编码习惯的人,尤为致命接口
C:对线程池[会复用core线程的那种]或者执行耗时较长的线程,慎用!内存
=============================分割线==================================
知道利弊以后说实现原理,ThreadLocal实际上等同于一个2级的hashMap
一、每一个Thread类里面都有个属性成员:threadLocals,定义为:ThreadLocal.ThreadLocalMap,再深刻则为WeakReference<ThreadLocal<?>>的子类:Entry(ThreadLocal<?> k, Object obj), 实现了对Thread的弱引用和对线程局部变量obj的强引用
二、ThreadLocal<?>存储的就是threadLocals,由于是以线程为key的因此线程安全,也就意味着只要代码所在的实例是属于同一线程的就能随时获取
三、ThreadLocal的get方法就是执行Thread t = Thread.currentThread(); return t.threadLocals,所谓的线程副本也就是这么来的了
四、注意ThreadLocal在么有set以前get的是null,除非实现initialValue方法进行初始化。
==================分割线========================================
知道原理以后咱们说说坑,以下1和2都是会致使内存泄露的大坑:
一、无良好编码习惯的人,会有一个懒操做:就是使用完ThreadLocal退出前不手动执行remove方法进行释放
固然此举在不少不少不少状况下,都是不会有问题的,由于线程是会快速消亡的,因此线程副本的内容也是会跟着gc随风而去的,
可是,可是有些时候就是不行的,好比由controller引入的tomcat发起的http-bio的线程,这种线程是池模式的,是复用的。但凡一个URL,其http-bio-exec-XX的线程都是存活好久的,由该线程串起来的controller、service和dao都是同一个线程副本,你不释放,tomcat复用此线程的时候......悲剧就发生了
二、自实现线程池的时候,同上原理,你使用了线程副本又不手动释放,是打算坐等其随风而逝么?
因此,手动remove,在finally里面手动remove掉使用完的线程副本,!
三、ThreadLocal说的是整个线程内获取,可是若是你在某个service服务类里面建立了子线程,怎么办?获取不到了啊就是。
可使用InheritableThreadLocal,这样子线程也能获取父线程的局部变量了就,其余ThreadLocal的子类用处待扩展
============分割线=================
一、扩展如Exchanger 安全的交互两个线程之间的数据[区分单槽交换slotExchange和多槽交换arenaExchange]
二、SuppliedThreadLocal--JDK 1.8 引入的函数式接口,配合静态withInitial方法,值得ThreadLocal也能够进行lamda编程?
例如:ThreadLocal<Long> threadLocal = ThreadLocal.withInitial(System::currentTimeMillis);
System.out.println(threadLocal.get());