背景:
在一次扫描中被提示: Field [SESSION_CONTEXT] of type ThreadLocal must call remove() method at least one times. (line 34)
嗯?啥子情况?
搜索了一下,发现:
ThreadLocal 属于线程,于线程创建而生,线程结束而自然销毁,本来是没什么问题的。
但是, 但是如果是在线程池中的线程,就会产生问题。
因为:线程池里面的线程是重复使用的,线程并不会因为用完了销毁,而是会放回线程池,那意思就是说,这个 ThreadLocal 里面放的数据,它也在!如果下一个请求来了,还是从线程池中拿到这个线程,那即使它还没有产生任何数据,ThreadLocal 里面却已经存在了上一次请求的数据,这个时候,可能会出现问题。
为什么说是 “可能”呢?
因为这得分情况来看:
情况1:请求进来,使用ThreadLocal,先 set 再 get , 这个时候,没有问题。因为先写再读,它即使一开始带了上个请求的数据,但会更新为自己正确的数据,再读取,oK,没有问题。
情况2:请求进来,使用ThreadLocal,先 get ,做一些操作了,再 set , 那么这个时候,就会有问题了,因为它会拿到上个请求的数据去做自己的业务,这可能就会产生奇奇怪怪的问题了。
怎么办?
使用 ThreadLocal, 每次用完后,就调用 remove 方法, 将其删掉,避免产生先 get 后 set 的情况,导致业务逻辑错误,还不太好排查。
用完就清,还有一个好处:
就是能够比较好避免堆溢出。原因,也不难理解,因为如果每个线程进来,用了又不删除掉,也无法释放,那这个 ThreadLocal (也就是一个Map),放的数据就会越来越多,占用堆内存也会越来越大。
引用:
《Java并发编程实战》一书的第8章时,有如下一句话:
只有当线程本地值的生命周期受限于任务的生命周期时,在线程池的线程中使用ThreadLocal才有意义,而在线程池的线程中不应该使用ThreadLocal在任务之间传递值。
嗯。反正 ThreadLocal 以后使用时,无论是先 get 还是 先 set 都建议用完清空一下。
(记录一下)
再说多两句,
说是说 ThreadLocal 用完要 remove, 但这个边界—— 什么时候才算是用完呢?
一开始确实是迷惑了一下,如果还没有用完,先 remove 掉了,那肯定会出问题的。
说一下,我遇到的这个场景,
这个 case 是在一个 controller 拦截器上做的业务,进入 controller 方法前,做一些判断,然后通过了就进入 controller, 然后再到下一个请求进来这个 controller。
那这个场景下,什么时候算是结束?
我理解就是:这个拦截切面 @after 方法就好了。完成 controller 方法之后。