记录一次ThreadLocal使用的坑——Web项目中使用ThreadLocal的坑

ThreadLocal 简介

ThreadLocal是一个本地线程副本变量工具类。主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不同的变量值完成操作的场景。

这个是ThreadLocal 详细介绍

场景还原

我们公司是根据用户来进行分库处理的,所以我选择使用AOP进行拦截用户请求,通过用户传递过来的Token去调用其他服务进行验证(较为耗时),并获得该用户数据库名称,然后将数据库名称塞到参数列表里去。因为切面是切向所有ServiceImpl,所以会出现,A方法调用B方法时,又进行AOP拦截,又去验证住户信息。为了防止重复验证住户信息,所以使用了ThreadLocal将该住户的信息Set进ThreadLocal。这样既可以保证同一个请求中如果ThreadLocal中有值,那么就不会去重复验证,一切都在我以为当中,上测试环境,一开始没觉得,后来测试过来跟我说,使用其他账号登录时,居然数据跟之前账号是一样的!

排查过程

首先我想到是不是前端把住户信息写死了,或者拿错了?(首先怀疑别人😂)。发现没有!
有没有可能是后台写死了?不可能,因为我每次都是拿的用户信息。配置文件里,不可能写死。
然后我打开日志一看,发现居然ThreadLocal里拿到的用户信息居然是上一个用户的。我很纳闷,不是说好每条线程都有自己的独立空间吗!怎么突然错乱了?
然后上网开始找文章,发现网上大多是说使用ThreadLocal一定要记得remove,避免内存泄露的。

直到我看到这一篇文章:
ThreadLocal不调用remove方法会导致业务逻辑错误
才发现这个web当中使用ThreadLocal的坑

问题所在

原来是因为web项目中,Tomcat这类容器都会维护一个线程池,也就是说web项目他的线程是可以得到复用的,问题就出在这!因为ThreadLocal是一个特殊的Map,他的key是当前线程,value是我们自己设置的值。所以我在方法中没有调用remove,导致被复用的线程又拿到了旧的值,才导致错误的发生

解决办法

  • 有人想,既然这样,你调用一下Remove方法不就可以了吗?当然不是,因为我的Aop切面切的是所有Service方法,如果每次只在拦截时,设置值,然后删除值,岂不是无用功。当然你也可以在写个过滤器或者Aop拦截返回方法,把ThreadLocal里的值干掉。但是我觉得麻烦!
  • 所以我使用第二种方法,既然你存储的是当前线程,我只要设置一个过期时间,那么等下一次复用时,我判断这个时间是否相等,如果相等说明还是在同一个方法里,如果没有,说明过期了,我就将它删除即可
    这里是这样实现:
    1. 获取当前毫秒值,并放入ThreadLocal
    2. 使用Redis 设置key=毫秒值,value=住户信息,过期时间设置为1秒
    3.这样每次验证时,先拿到ThreadLocal里的毫秒值,再去Reids中拿,看是否能获取到,如果获取不到,说明已经过期,调用ThreadLocal的Remove方法删除,然后重新执行1,2方法。这样就完美解决了

最后

本人小白,有什么不对的地方,请各位大佬指教!

  • 6
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值