1.背景
大家好,我是阿骏。今天分享一个我们项目中遇到的问题。
详细过程是这样的:开发了一个新需求,需求是用户针对某个业务提交申请,涉及这个业务的管理员进行审批。我们的同学三下五除二开发自测完后发现没问题,但是提交测试后,测试的同学反馈管理员登录后在进行流程审批时有时候能够看到不属于自己审核的流程,刷新后看到的数据又正确,再刷新又不正确了。。。真是个怪事儿啊!
2.排查过程
我们排查了相关逻辑代码–>没问题,再排查sql–>还是没问题啊。折腾半天实在找不出问题了,最后技术leader排查发现使用上下文拿到的用户信息不是当前登陆用户的,而是其他用户的信息。
我们项目是使用的ThreadLocal
暂存当前用户的相关信息,便于我们在业务逻辑处理时便捷地拿到用户的信息。所以最后精准定位了是ThreadLocal
使用不当导致的内存泄露!
3.什么是ThreadLocal
ThreadLocal
是Java中的一个线程级别的变量,它提供了一种在多线程环境下存储和访问线程局部变量的机制。每个线程都有自己独立的ThreadLocal
实例,线程可以通过ThreadLocal
实例来存储和获取自己线程内部的数据。
在Web应用程序中,每个请求通常会由一个独立的线程来处理。当一个请求结束后,线程会被释放并返回到线程池中,等待下一个请求的到来。在这个过程中,如果使用了ThreadLocal
来存储用户信息,那么在请求结束后,ThreadLocal
实例会被垃圾回收器(GC)
回收。
然而,尽管ThreadLocal
实例被回收了,但是在同一个线程内部的其他请求仍然可以访问到之前设置的值。这是因为在每个线程内部,ThreadLocal
实例是以ThreadLocalMap
的形式存储的,而ThreadLocalMap
是与线程绑定的,不会被回收。
当一个请求结束后,线程会被放回线程池中,但是线程的ThreadLocalMap
并不会被清空,而是等待下一个请求的到来。当下一个请求到来时,线程会从线程池中取出,并且ThreadLocalMap
中仍然保留着上一个请求设置的值,因此可以获取到上一个用户的信息。
4.使用ThreadLocal
注意事项
需要注意的是,如果没有正确地清理ThreadLocal
的值,可能会导致内存泄漏。因此,在使用ThreadLocal
时,需要确保在每个请求结束后,手动清理ThreadLocal
的值,以避免潜在的内存泄漏问题。请务必在请求处理的最后,通过调用ThreadLocal的remove方法来清理ThreadLocal的值。