一.问题
在使用ThreadLocal做线程间数据传递的过程中,发现有的用户数据出现了异常
二.分析问题
1.理论上来说ThreadLocal是线程安全的
2.只有在暴露ThreadLocal数据时多线程对其修改,或者内存溢出才会导致线程安全问题
3.知道这些先来看一下具体业务代码
a.这是一个导出Excel任务
b.监听查询任务,如果任务超时,则异步执行
c.由于Session的问题所以采用ThreadLocal进行线程间数据传输
似乎没有什么问题,但是有些用户的权限似乎出现了问题,并且是偶发性的,然后定位到了这段代码,因为查询操作从头到尾没有使用过TheadLocal中的信息,理论上来说这里的requestSession这个Map一定是null,但是在后续的debug中发现查询操作居然进入了第一个判断
三.原因猜测
- 从上面的分析可以看出来,查询操作没有使用到ThreadLocal,但是其中却有数据,说明是在其他地方进行了set操作
- 然后仔细检查了所有的拦截器,是否存在使用了ThreadLocal的情况,最终没有发现
- 自此就非常奇怪了,ThreadLocal线程安全,所以理论上线程见的数据不会相互影响
-
查看ThreadLocal的set源码,这里感觉非常的奇怪,ThreadLocal是使用当前线程作为key,然后返回了一个ThreadLocalMap,然后将数据写了进去,这让我感到非常的奇怪,那如果有用户当前线程的名称一样,那线程不就不安全了吗,所以这个问题就变成了SpringMVC是否会复用线程?
-
这里写了个demo
执行第一个请求10次之后,发现SpringMVC开始复用线程了,这让我很吃惊,这时候请求第二个接口,发现居然从ThreadLocal中取到了数据,SpringMVC这么做自然是为了避免资源的浪费
四.总结
- ThreadLocal确实是线程安全的,但是和SpringMVC配合使用的时候,springMVC复用了线程,导致ThreadLocal不再线程安全
- 在使用完ThreadLocal之后一定要执行remove方法,不管任务知否执行成功