1.什么是ThreadLocal? 如何实现的
ThreadLocal为java下的一个类,主要用于解决线程间数据隔离的问题。简单来说,它为每个线程提供了一个独立的变量副本,使得每个线程在访问该变量时,实际上获取到的是自己线程独有的副本,而不是其他线程可见的共享变量。这样就避免了线程间的竞态条件和数据同步问题,尤其适用于那些需要在线程内保持状态,但又不希望这个状态被其他线程干扰或者影响的情况。
总结来说 就是可以用来解决多线程程序中并发问题, 每个线程共享一个变量来保证各个线程之间的变量访问与修改, 其中线程之间互相不影响。
ThreadLocal为每个使用它的线程提供了一个独立的变量副本。每个线程都通过其自身的ThreadLocalMap(一个定制化的哈希表)来存储这些副本。当一个线程第一次读取某个ThreadLocal变量时,如果当前线程的ThreadLocalMap中尚不存在对应的键值对,就会创建该变量的副本,并将其放入ThreadLocalMap中。后续对该变量的所有操作(如读取、更新等)都将针对这个副本进行,与其他线程完全隔离。
我们关注ThreadLocal只需要关注一个内部的map. 也是基于K-V结构 key就是当前的ThreadLocal对象,而v就是保存想要保存的值(比如token 用户信息等) , 无需用其他的代码访问
2.出现的问题
1.ThreadLocal在中会出现内存泄露, 这是一个比较典型的问题
会导致ThreadLocal内存泄漏的部分其实就是他在堆上存储的ThreadLocalMap中的K-V部分
ThreadLocalMap的key就是ThreadLocal对象,他有两个引用源,一个是栈上的ThreadLocal引用,一个是ThreadLocalMap中的Key对他的引用。
而对于value来说,他的引用就一条,就是从Thread对象过来的。
所以,就会出现以下两种情况:
1、栈上的ThreadLocal Ref引用不在使用了,即方法结束后这个对象引用就不再用了,那么,ThreadLocal对象因为还有一条引用链在,所以就会导致他无法被回收,久而久之可能就会对导致OOM。
2、Thread对象如果一直在被使用,比如在线程池中被重复使用,那么从这条引用链就一直在,那么就会导致ThreadLocalMap无法被回收。
弱引用解决内存泄漏
我们先来说说JDK自己帮我们实现的一部分功能,主要是解决上面说的第一种情况:
栈上的ThreadLocal Ref引用不在使用了,即方法结束后这个对象引用就不再用了,那么,ThreadLocal对象因为还有一条引用链在,所以就会导致他无法被回收,久而久之可能就会对导致OOM。
为了解决这个问题,ThreadLocalMap使用了弱引用。
因为我们可以看到,虽然key是弱引用,但是value的那条引用,还是个强引用呢!而且他的生命周期是和Thread一样的,也就是说,只要这个Thread还在, 这个对象就无法被回收。
那么,什么情况下,Thread会一直在呢?那就是线程池。
在线程池中,重复利用线程的时候,就会导致这个引用一直在,而value就一直无法被回收。
那么如何解决呢?
ThreadLocalMap底层使用数组来保存元素,使用“线性探测法”来解决hash冲突的,在每次调用ThreadLocal的get、set、remove等方法的时候,内部会实际调用ThreadLocalMap的get、set、remove等操作。
而ThreadLocalMap的每次get、set、remove,都会清理key为null,但是value还存在的Entry。
所以,当我们在一个ThreadLocal用完之后,手动调用一下remove,就可以在下一次GC的时候,把Entry清理掉。