首页 > 其他分享 >记一次ThreadLocal中的用户信息混乱问题

记一次ThreadLocal中的用户信息混乱问题

时间:2024-05-14 11:10:21浏览次数:19  
标签:用户 信息 ThreadLocal 线程 内存 混乱 public

前言

记录一次开发中遇到的关于 ThreadLocal 问题,场景是数据库表中的操作人总是无缘无故的被更改,排查了几遍代码才发现是 ThreadLocal 没有及时清理导致的。


一、为什么使用 ThreadLocal

1. ThreadLocal 的好处

一般的项目设计开发中,用户登录后,我们会将用户的信息存到 Session,如果想在其它地方获取用户信息,需要频繁的传递 Session 对象。如果遇到高并发,多人同时登录系统时,可能会出现 Session 混乱,导致获取的用户信息不一致。


而 ThreadLocal 可以将用户信息保存在本地线程变量中,当线程结束后我们在把用户信息清理掉。这样在进行开发时,就可以很方便的从全局获取用户信息,不需要频繁的传递 Session 对象,提升开发效率。


并且 ThreadLocal 是线程隔离的,每个线程都有自己独立的变量副本,不会受到其他线程的影响,可以避免线程安全问题。

2. 注意事项

ThreadLocal 也是有缺点的,有两个比较突出的缺点是内存泄漏和上下文切换问题:

  • 内存泄漏:ThreadLocal 是与线程绑定的,如果线程一直存在,那么对应的变量副本也会一直存在,可能会占用大量的内存空间,如不及时清理 ,可能会导致内存泄漏问题。

  • 上下文切换问题:由于每个线程都有自己的变量副本,当需要在多个线程之间共享数据时,可能需要进行额外的上下文切换操作,增加了程序的复杂性和开销。

所以我们在业务逻辑结束时,一定要清理一下 ThreadLocal 的数据

3. 如何实现

不管过滤器还是拦截器,只要是能拦截住请求,获取到用户信息均可,这里简单介绍拦截器的实现方法。


创建用户信息工具类

public class CurrentUserUtil {

    /**
     * 初始化用户对象的ThreadLocal
     */
    private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();

    /**
     * 添加当前登录用户方法
     */
    public static void addCurrentUser(User user){
        userThreadLocal.set(user);
    }

    public static User getCurrentUser(){
        return userThreadLocal.get();
    }


    /**
     * 防止内存泄漏
     */
    public static void remove(){
        userThreadLocal.remove();
    }

}

拦截器中调用,在preHandle()方法中根据业务需求将用户的登录信息存放之 ThreadLocal 中即可。然后最后记得清除相关数据以避免内存泄漏。

public class UnifyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        User user = new User();
        user.setWorkNo("123456");
        CurrentUserUtil.addCurrentUser(user);
        return true;
    }

    /**
     * 避免内存泄露
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        
        UserInfoThreadHolder.remove();
    }
}


这里关于拦截器的详细使用不多做赘述,详细介绍见Spring 的过滤器和拦截器

二、问题记录

测试同学在项目合同签约完以后,发现签约完成后数字资产的 Owner 被改变了。然后去排查代码,开始并没有发现代码有问题,于是去排查 ThreadLocal 中的用户信息,发现是操作人已经不一致。所以在执行后续操作时数据库中的操作人就被修改了。


按理说,在设置用户信息之前第一次获取的值始终应该是 null,但是请求线程被 Tomcat 回收后,不一定会立即销毁,如果不在请求结束后主动 remove 线程中的 ThreadLocal 信息,可能会影响后续逻辑,拿到脏数据。


这里我还犯了一个小错误,我知道 ThreadLocal 会产生内存泄漏,需要进行清除操作,但是我把它放在方法执行前了,也就是说每次请求会现进行清除操作,正常流程是不会出错的。


但是当我们通过 MQ 消息队列接受消息时,按理说此时的 ThreadLocal 里的用户信息应该为 null,但由于没有在方法执行结束前及时清理 ThreadLocal,导致了用户信息出现了不一致的情况。后续我将问题 clear 方法放在了结束时执行,并在消息监听的方法里也进行了清理保证不会被其他用户信息所干扰。


所以,不论使用 ThreadLocal 存什么数据,请务必记得,在业务逻辑结束之前清理 ThreadLocal 中的数据。

标签:用户,信息,ThreadLocal,线程,内存,混乱,public
From: https://www.cnblogs.com/fuxing/p/18190900

相关文章

  • ZYNQ核心板用户手册
    本人历经3个多月的辛苦设计的一款开发板,定位于初学者的入门学习以及功能开发验证使用。从原理图设计,PCB设计,PCB加工,贴装,板卡终于到手,正在进行接口功能调试,调试完成后将送出一波福利,欢迎大家关注。友情提示:文档内容较长,电脑端阅读体验更佳!0,文档管理此文档由【数字积木】编写。......
  • root用户远程登录云服务器失败 No supported authentication methods available (serv
     1、平台:亚马逊AWS云、腾讯云服务器、MobaXterm2、问题:云服务器实例远程登录失败,显示:“Nosupportedauthenticationmethodsavailable(serversent:publickey)”翻译:不支持可用的身份验证方法(服务器发送:publickey)3、解决过程:初步判断:服务器远程登录配置文件问题尝试1:a.......
  • 【django学习-20】新增用户(ModelForm方式)
    1.什么是ModelForm?ModelForm顾名思义就Form和Django的Model数据库模型结合体,可以简单、方便得对数据库进行增加、编辑操作和验证标签的生成;比如我们的数据库中有这样一张学生表,字段有姓名,年龄,爱好,邮箱,电话,住址,注册时间等等一大堆信息,现在让你写一个创建学生的页面,你的后台应......
  • 【django学习-19】基本流程与用户管理界面(原始方式)
    1.安装及创建项目1.1:安装django,pipinstalldjango1.2:创建项目:django-adminstartproject项目名称1.3:创建app:pythonmanage.pystartappapp名称1.4:使用pychram创建项目:1.4.1:注意点,pycharm在标准的基础上默认给咱们加了点东西1.4.2:创建了一个templates目录【删除】1.......
  • 【M5Stack物联网开发】第四章 用户界面
    1交互设计交互设计(InteractionDesign,简称IxD)是一种专注于创造有意义的关系,介于人与人、人与产品或服务之间的设计领域。这种设计形式主要关注于如何使产品、系统或服务与用户之间的交互更加有效、效率高、直观和愉悦。交互设计的核心目标是提高用户体验和满意度,通过改善产品的......
  • 【TransmittableThreadLocal】TransmittableThreadLocal的实现机制和原理
    1 前言前面我看过了 ThreadLocal的实现机制和原理 以及 InheritableThreadLocal的实现机制和原理 两种类型的ThreadLocal,前者是普通的,后者是在前者的基础上套了一层父子线程关系,当使用后者的时候,会在线程创建的时候,浅拷贝一份父线程的变量值。那么今天空了,我来看看另外一......
  • PLC的开放式用户协议
    1.OUC开放式用户协议,包括ISO,ISO-on-TCP,TCP/IP,UDP四种。西门子PLC中有多种不同的方式建立连接。2.在硬件组态中建立TCP通信打开硬件组图,网络视图添加新连接填入本地ID,关于本地ID:针对1513实测过,ID的取值范围从16#01到16#999,但是16#01-16#99大概率被系统占用了,可用范围从1......
  • ThreadLocal入门笔记
    ThreadLocal入门笔记最近学习小傅哥的面经手册,学习到ThreadLocal,这里做个笔记加深印象,也方便日后复习。ThreadLocal是除了加锁这种同步方式之外的一种规避多线程访问出现线程不安全的方法,它的核心思想是:共享变量在每个线程都有一个副本,每个线程操作的都是自己的副本,对另外的线程......
  • FM20S用户手册-Linux开发环境搭建
     ......
  • ABP-VNext 用户权限管理系统实战06---实体的创建标准及迁移
    在apb-vnext的实体的创建中可以确实字段的长度、说明、对应的表、表中给字段加的索引以项目中的订单表为例,如下:[Comment("订单主表")][Table("t_voucher_order")] [Index(nameof(VoucherCode))]publicclassOrder:AuditedAggregateRoot<Guid>,ISoftDelete,IMultiTen......