ThreadLocal存List遇到的坑

之前为了方便一个Service下共用List,将List作为类成员变量,当然有点线程安全意识的兄弟们都知道不能就这么直接加在上面。

1 public class PermService {
3     private List<String> list;
5     ....    
6 }

当然用锁(synchronized)又还不至于,于是这里用了线程本地变量ThreadLocal,如下:

 1 public class PermService {
 2 
 3     private static final ThreadLocal<List<String>> STR_LIST = new ThreadLocal<List<String>>() {
 4         @Override
 5         protected List<String> initialValue() {
 6             return new ArrayList<String>();
 7         }
 8     };
 9     ....  
10 }

然后我每次请求进来之后都是类似于这么处理的

1 public void storePermsByRole(String roleId) {
2   List<String> permIds = rolePermMapper.selectPermByRole(roleId);
3   STR_LIST.get().addAll(permIds);
4 }

后面用postman测的时候问题来了:原本和这个roleId没有关系的permId在返回体里面出现了,查了下库里面也没有这两个关系的记录,debug断点打到上面的第3行permIds里面也没有那一条数据,那么这多出来的一条是哪来的呢?

 

苦思冥想意识到可能和线程池有关系(这里说的线程池不是你配的全局线程池,也不是手动用Executors生成的线程池),所有线程池一般有个核心线程数,这些线程是不会在一个请求request结束后就销毁的,而线程本地变量又是依附于线程的(具体可以理解下ThreadLocalMap),所以一个线程上一个请求将数据库中查出来的list加到线程本地变量后,下一个请求进来原来的list还在里面,从而导致了上面的问题。

这里解决方法是在service中加一段清空ThreadLocal的逻辑,在请求结束之前调一下即可。

1 public void clearThreadLocal() {
2   STR_LIST.remove();
3 }

 

ThreadLocal用的守则:

1、请求开始时set

2、请求结束前remove

3、加static关键字

 

posted @ 2019-04-17 10:26  JN_Oldog  阅读(1730)  评论(2编辑  收藏  举报