ThreadLocal概述
常常会在资料上看到对ThreadLocal的描述:
- “是一个变量的本地副本,为每一个线程提供一个变量副本,相互不影响”
- “避免了共享变量的冲突”
- “解决多线程的并发访问的一种方式”
个人觉得这样的描述很具有迷惑性,”副本”那主本是什么?它根本就不是解决”并发访问”问题的好吧,ThreadLocal中的变量根本就没有共享,哪来的”并发访问”?
更确切的定义:
ThreadLocal是线程执行时的上下文,用于存放线程局部变量。
ThreadLocal 涉及的三个类:
- ThreadLocalMap 是存放局部变量的容器
- Thread中通过变量ThreadLocal.ThreadLocalMap threadLocals来持有ThreadLocalMap的实例
- ThreadLocal则是ThreadLocalMap的manager,控制着ThreadLocalMap的创建、存取、删除等工作。
ThreadLocalMap
ThreadLocalMap和Map接口没有关系,它是使用数组来存储变量的,定义如下:
private Entry[] table //table的初始容量是16
当table的实际长度大于容量时进行成倍扩容,所以table的容量始终是2的幂。
Entry使用ThreadLocal对象作为键,注意不是使用线程(Thread)对象作为键。Entry类继承了WeakReference,定义成弱引用的目的是确保没有了ThreadLocal对象的强引用时,能释放ThreadLocalMap中的变量内存。
由于ThreadLocalMap是隐藏在内部的,程序员不可见,所以必须要有一个机制能释放ThreadLocalMap对象中的变量内存。如下场景:
// 定义ThreadLocal
public static final ThreadLocal<Session> sessions = new ThreadLocal<Session>();
在某个时刻:
sessions = null;
内存泄露
ThreadLocal为应对内存泄露做的工作:
- 将Entry定义成弱引用,如果ThreadLocal实例不存在强引用了那么Entry的key就会失效
- 代码实现中取值方法(get)、存入值方法(set)执行前都对失效的Key进行清理
即便是这样也不能保证万无一失:
- 一般情况下为了使线程可以共用ThreadLocal,会这样定义:static final ThreadLocal threadLocal,这样static变量的生命周期是随class一起的,所以它永远不会被GC回收,这个强引用在key就不会失效。
- 不使用static定义threadLocal,由于有一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value的存在,在长生命周期的线程中(列如线程池)也有内存泄露的风险。短生命周期的线程则无所谓,由于随着线程生命周期的结束,一切都烟消云散。
当然这并不可怕,只要在使用完threadLocal后调用下remove()方法,清除数据,就可以了。
小结
1:ThreadLocal是线程执行时的上下文,用于存放线程局部变量。它不能解决并发情况下数据共享的问题
2:ThreadLocal是以ThreadLocal对象本身作为key的,不是线程(Thread)对象
3:ThreadLocal存在内存泄露的风险,要养成用完即删的习惯
4:ThreadLocal使用散列定位数据存储坐标,如果发生Hash碰撞,使用线性探测重新定位,这在高并发场景下会影响一点性能。改善方法如netty的FastThreadLocal,使用固定坐标,以空间换时间。




