ThreadLocal会为每个线程分配一个变量副本,在Thread类中通过ThreadLocalMap进行保存,key为ThreadLocal对象,value为设置的值,其中key为弱引用。
接下来要从以下2个方面进行讨论:
1、ThreadLocal的生命周期长短
注:此处ThreadLocal生命周期短指的是ThreadLocal是局部变量或者ThreadLocal为成员变量时,所在的类是多例的,可以理解为一次请求就会创建一个对象,请求完成后对象会被销毁。
ThreadLocal生命周期长指的是ThreadLocal被static修饰,或者ThreadLocal为成员变量时,所在的类是单例的,可以理解为每次请求都是这一个对象,比如spring中的单例。
2、程序中是否使用了线程池
首先来讨论2,如果程序未使用线程池,那么线程会在执行完后自动销毁,即内部的成员ThreadLocalMap也会被回收,此时无论1中ThreadLocal生命周期长短都不会有影响。
如果2中使用了线程池:当1中ThreadLocal生命周期短时,那么在一次请求结束后,ThreadLocal对象将不会有强引用,在进行GC的时候,ThreadLocalMap的key指向的ThreadLocal将被回收,但是value得不到释放,所以这种情况需要执行ThreadLocal.remove()手动释放。
当1中ThreadLocal生命周期长时,ThreadLocal将长时间持有强引用(一般在项目里ThreadLocal使用的是这种方式),那么在进行GC的时候,ThreadLocalMap的key将长时间无法回收,这种情况也需要执行ThreadLocal.remove()手动释放。
综上所述,情况可以分为三类:
1、上述2中未使用线程池
2、上述2中使用线程池,1中ThreadLocal生命周期短
3、上述2中使用线程池,1中ThreadLocal生命周期长
对于情况1来说,使用完ThreadLocal后,可以不使用ThreadLocal.remove()手动释放。
对于情况2、3来说,使用完ThreadLocal后,必须使用ThreadLocal.remove()进行手动释放。
但你无法确定以后项目里会不会使用线程池?如果以后使用了,而未执行ThreadLocal.remove(),那将是一个隐患,所以建议情况1也调用remove手动释放。
结论:如果你能保证项目一定不会用到线程池,可以不用ThreadLocal.remove()手动释放,否则必须要手动释放。
关于ThreadLocalMap的key为何要设置为弱引用,从上述的情况2也能看出,当线程要运行很长时间时(线程池),通过GC可以释放key所占用的内存。