之前为了方便一个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关键字