ThreadLocal是什么
ThreadLocal 用于提供线程局部变量,在多线程环境可以保证各个线程里的变量独立于其它线程里的变量。也就是说 ThreadLocal 可以为每个线程创建一个【单独的变量副本】。实现了线程的数据隔离。
简单来说就是:往ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal实现原理
ThreadLocal的内部结构如下图
ThreadLocal并不维护ThreadLocalMap,并不是一个存储数据的容器,它只是相当于一个工具包,提供了操作该容器的方法,如get、set、remove等。而ThreadLocal内部类ThreadLocalMap才是存储数据的容器,并且该容器由Thread维护。
每一个Thread对象均含有一个ThreadLocalMap类型的成员变量threadLocals,它存储本线程中所有ThreadLocal对象及其对应的值
set(T value)
1 | public void set(T value) { |
获取当前线程,然后在获取到当前线程的 ThreadLocalMap,如果 ThreadLocalMap 不为 null,则将 value 保存到 ThreadLocalMap 中,并用当前 ThreadLocal 作为 key;否则创建一个 ThreadLocalMap 并给到当前线程,然后保存 value
T get()
1 | public T get() { |
get方法执行过程与上面类似。ThreadLocal首先会获取当前线程对象,然后获取当前线程的ThreadLocalMap对象。再以当前ThreadLocal对象为key,获取对应的value.
remove()
1 | public void remove() { |
remove() 方法,通过this ThreadLocal为键来移除ThreadLocalMap中对应的值。
内存泄漏问题
Entry中的key为一个threadlocal实例. 这个Entry使用了弱引用,不过弱引用只是针对key. 每个key弱引用指向threadlocal. 当把threadLocal引用(threadLocalRef)置为null以后,threadLocal就没有一条引用链路可达,很显然在gc(垃圾回收)的时候势必会被回收,因此entry就存在key为null的情况,无法通过一个key为null去访问到该entry的value
但是,与此同时我们的value却不能被回收,因为存在一条从CurrentThreadRef连接过来的强引用. 只有当前thread结束以后, CurrentThreadRef就不会存在栈中,强引用断开,gc进行可达性分析的时候, Current Thread, ThreadLocalMap, value valueMemory将全部被GC回收。就不会出现内存泄露。
但有时候使用线程池的时候,线程结束是不会销毁的,会再次使用的就可能出现内存泄露 (value访问不到,同时又回收不到)(比如在web应用中,每次http请求都是一个线程,tomcat容器配置使用线程池时会出现内存泄漏问题)
所以想要避免内存泄露就应该使用完threadLocal之后,最后手动remove()掉!总结
- TreadLocalMap是ThreadLocal的内部类,用Entry来进行存储
- 每个Thread维护着一个ThreadLocalMap的引用
- 调用ThreadLocal的set()方法时,实际上就是往当前线程的ThreadLocalMap设置值,key是ThreadLocal对象,值是传递进来的对象
- 调用ThreadLocal的get()方法时,实际上就是往ThreadLocalMap获取值,key是ThreadLocal对象
- ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value