不规范使用ThreadLocal导致bug分析解决

目录
  • 因为线程重用导致的信息错乱的bug
  • 正确使用的姿势
  • 更优雅的处理方式
  • 最后

因为线程重用导致的信息错乱的bug

ThreadLocal一般用于线程间的数据隔离,通过将数据缓存在ThreadLocal中,可以极大的提升性能。但是,如果错误的使用Threadlocal,可能会引起不可预期的bug,以及造成内存泄露。

有时我们会在一个接口中缓存某些数据到ThreadLocal中,但是我们要意识到,处理请求的这些线程是由tomcat提供的,而tomcat提供的线程都是配置在一个线程池中的。

也就是说,线程是可能被重用的,如果线程一旦被重用,而ThreadLocal的数据没有及时重置,就会导致数据被混乱使用。

以下方的接口为例,先获取当前线程中保存的数据信息,将参数中的name保存到ThreadLocal中以后,再获取一次。

@GetMapping(value = "/threadLocal")
public ResponseEntity<Object> threadLocal(String name) {
  String before = Thread.currentThread().getName() + ":" + threadLocal.get();
  //先获取值,理论上应该是null
  System.out.println("before:" + before);
  threadLocal.set(name);
  String after = Thread.currentThread().getName() + ":" + threadLocal.get();
  //设置完参数值再获取一次
  System.out.println("after:" + after);
  return ResponseEntity.ok().build();
}

为了尽快复现线程重用导致的问题,我们将servlet.tomcat.threads.max设置为1,这样每次请求使用的都是同一个线程。

第一次请求接口,数据看起来很正常:

但是第二次请求接口时,可以看到线程仍然是http-nio-8080-exec-1,但是before却打印出了第一次请求的参数test。

这就是因为没有及时重置ThreadLocal导致的数据错误。

正确使用的姿势

修正的办法就是处理完接口之后要及时清理ThreadLocal。

@GetMapping(value = "/threadLocal")
public ResponseEntity<Object> threadLocal(String name) {
  try {
    String before = Thread.currentThread().getName() + ":" + threadLocal.get();
    //先获取值,理论上应该是null
    System.out.println("before:" + before);
    threadLocal.set(name);
    String after = Thread.currentThread().getName() + ":" + threadLocal.get();
    //设置完参数值再获取一次
    System.out.println("after:" + after);
  } finally {
    //清理数据
    threadLocal.remove();
  }
  return ResponseEntity.ok().build();
}

更优雅的处理方式

可能也有的朋友会说,每次都要使用try finally处理线程数据,未免也太麻烦了。其实,我们可以使用拦截器或者过滤器自动帮我们完成数据的初始化以及清理工作。

最后

我们在写业务代码时,正确的理解线程的全生命周期以及执行原理,对我们提升代码的健壮性其实很有帮助。有时我们觉得底层原理很枯燥乏味,开发业务就是写增删改查,多线程用的也很少,但我们只是没有意识到,我们的代码一直跑在tomcat提供的线程池中,本身就是一个多线程的环境。

除了tomcat的线程池,我们自定义的线程池其实也会有这个问题,大家可以看看自己的业务代码是否踩过这个坑。

以上就是不规范使用ThreadLocal导致bug分析解决的详细内容,更多关于ThreadLocal bug解决分析的资料请关注我们其它相关文章!

(0)

相关推荐

  • TransmittableThreadLocal解决线程间上下文传递烦恼

    目录 前言 示例 使用方式 小结 前言 在一些项目中,经常会遇到需要把当前线程中的上下文传递到其他线程中的情况,比如某项目包含国际化操作,在业务请求进来时需要把对应的国家代码存储到当前线程中,以便后续的业务逻辑能够根据国家代码正确地处理:另外在一些异步化操作中,也要保证异常线程中也能够正确地获取到对应的国家代码. 在上述业务场景中,我们很自然的就想到了使用ThreadLocal,但是ThreadLocal无法解决父子线程间上下文传递的问题,此时InheritableThreadLocal站出来了

  • Springboot公共字段填充及ThreadLocal模块改进方案

    目录 1. 公共字段自动填充 1.1 问题分析 1.2 基本功能实现 1.2.1 思路分析 1.3 代码实现 1). 实体类的属性上加入@TableField注解,指定自动填充的策略. 2). 按照框架要求编写元数据对象处理器,在此类中统一为公共字段赋值,此类需要实现MetaObjectHandler接口. 2 使用ThreadLocal对公共字段填充功能进行完善 2.1 思路分析 2.1.1 提出设想 2.1.2 分析问题 2.2 ThreadLocal 2.2.1 ThreadLocal基本

  • ThreadLocal数据存储结构原理解析

    目录 一:简述 二:TheadLocal的原理分析 1.ThreadLocal的存储结构 2.源码分析 set()方法 三:源码分析 createMap() 源码: 流程图: expungeStaleEntry() cleanSomeSlots() rehash() resize() get()方法 getEntry() getEntryAfterMiss() remove() 四:总结 一:简述 我们很多时候为了实现数据在线程级别下的隔离,会使用到ThreadLocal,那么TheadLoca

  • java开发工作中对InheritableThreadLocal使用思考

    目录 引言 1. 先说结论 2. 实验例子 3. 详解 3.1 JavaDoc 3.2 源码 3.2.1 childValue方法 4. 实验例子流程图 引言 最近在工作中结合线程池使用 InheritableThreadLocal 出现了获取线程变量“错误”的问题,看了相关的文档和源码后在此记录. 1. 先说结论 InheritableThreadLocal 只有在父线程创建子线程时,在子线程中才能获取到父线程中的线程变量: 当配合线程池使用时:“第一次在线程池中开启线程,能在子线程中获取到父

  • ThreadLocal作用原理与内存泄露示例解析

    目录 ThreadLocal作用 简单例子 局部变量.成员变量 . ThreadLocal.静态变量 共享 or 隔离 原理 源码分析 TheadLocal TheadLocalMap ThreadLocal与内存泄漏 小结 ThreadLocal作用 对于Android程序员来说,很多人都是在学习消息机制时候了解到ThreadLocal这个东西的.那它有什么作用呢?官方文档大致是这么描述的: ThreadLocal提供了线程局部变量 每个线程都拥有自己的变量副本,可以通过ThreadLocal

  • 不规范使用ThreadLocal导致bug分析解决

    目录 因为线程重用导致的信息错乱的bug 正确使用的姿势 更优雅的处理方式 最后 因为线程重用导致的信息错乱的bug ThreadLocal一般用于线程间的数据隔离,通过将数据缓存在ThreadLocal中,可以极大的提升性能.但是,如果错误的使用Threadlocal,可能会引起不可预期的bug,以及造成内存泄露. 有时我们会在一个接口中缓存某些数据到ThreadLocal中,但是我们要意识到,处理请求的这些线程是由tomcat提供的,而tomcat提供的线程都是配置在一个线程池中的. 也就是

  • 滥用@PathVariable导致bug原因分析解决

    目录 前言 复现 3个匹配步骤 1,根据Path精准匹配 2,如果精准匹配没有成功,就开始模糊匹配 3,如果模糊匹配还匹配不上,就返回null 最后 前言 最近测试同学反馈,上周上线的一个功能会偶然性的报404,按理说这个功能在测试环境已经测试通过,也在线上运行了好几天,怎么会突然报错呢. 一开始以为是前端同学请求的接口有误,但是测试又说只是偶然性的404,几率也不高,于是打开日志找到对应的接口,一眼看到了接口上定义的@PathVariable,再一看参数,基本就确定是开发同学为了偷懒又误用@P

  • Java中ThreadLocal 导致内存 OOM 的原因分析

    目录 原因分析 正确的使用方式 原因分析 ThreadLocal 导致内存 OOM 的原因是什么? ThreadLocal 底层通过 ThreadLocalMap 存储数据 源码如下:  当我们使用ThreadLocal.set()时,set的value与key(即业务自己定义的ThreadLocal类)会存储在ThreadLocalMap的Entry[]数组里 源码如下: 其中Entry是实现了一个弱引用WeakReference,Entry的key(即业务方定义的 ThreadLocal类)

  • Redis exists命令bug分析(案例详解)

    目录 1.复现条件版本: 2.源码分析 3.问题解决 本文基于社区版Redis 4.0.8 1.复现条件版本: 社区版Redis 4.0.10以下版本 使用场景:开启读写分离的主从架构或者集群架构(master只负责写流量,slave负责读流量) 案例: # 写入一条带过期时间10s的key 10.90.73.147:12345> set luxiu1 1 ex 10 OK 10.90.73.147:12345> get luxiu1 "1" 10.90.73.147:12

  • Python异常之常见的Bug类型解决方法

    目录 一.粗心导致的语法错误SyntaxError 1.input输入报错 2.循环语句报错 3.赋值报错 二.知识不熟练导致的错误Bug 1.索引越界问题 IndexError 2.append()函数的使用报错 三.思路不清晰导致的问题解决方案 1.使用print()函数 2.使用"#"暂时注销部分代码 四.被动掉坑 一.粗心导致的语法错误SyntaxError 1.input输入报错 age=input('请输入你的年龄:') if age>=18:     print(&

  • Android中EditText光标在4.0中的bug及解决方法

    本文分析了Android中EditText光标在4.0中的bug及解决方法.分享给大家供大家参考,具体如下: 一.问题: 不知道为什么,我的EditText的在我自己的手机中出现的时候,他的光标就变得没有了,我开始还以为是光标不见了,后面我修改了一下EditText的背景颜色,才发现是因为光标的颜色变成了白色,所以没有看见. 二.解决办法 : 复制代码 代码如下: android:textCursorDrawable 设置值为"@null",但是我觉得那个不怎么好看,那个地方也可以替换

  • vue2.0路由切换后页面滚动位置不变BUG的解决方法

    最近项目中遇到这样一个问题,vue切换路由,页面到顶端的滚动距离仍会保持不变. <a href="javascript:;" rel="external nofollow" class="btn btn01" @click="useRightNow">立即试用</a> <router-link class="db" to="/user">个人中心<

  • pytorch dataloader 取batch_size时候出现bug的解决方式

    1. RuntimeError: invalid argument 0: Sizes of tensors must match except in dimension 0. Got 342 and 281 in dimension 3 at /pytorch/aten/src/TH/generic/THTensorMoreMath.cpp:1333 2. RuntimeError: invalid argument 0: Sizes of tensors must match except i

  • Android Studio 3.5格式化布局代码时错位、错乱bug的解决

    更新到3.5版本后,格式化布局文件代码,会自动给排序元素,导致界面布局错乱 解决办法: 设置 > code style > XML 右上角 Set from然后选择Predefined Style...>Android即可 补充知识:Android Studio:Reformat Code格式化Xml布局代码后控件顺序错乱 Android Studio升级3.5之后,遇到个奇葩问题,在布局xml文件中格式化代码后,控件的顺序都变了,这不是我们想要的结果,网上搜了一下,确实是AS3.5的锅

  • 分析解决Python中sqlalchemy数据库连接池QueuePool异常

    目录 数据库相关错误的解决办法 错误一:数据库连接池超过限制 错误二:数据库事务未回滚 数据库相关错误的解决办法 错误一:数据库连接池超过限制 SqlAlchemy QueuePool limit overflow 造成连接数超过数据库连接池的限制,有两方面的原因,第一个是由于数据库连接池数比较小,因此当连接数稍微增加的时候就会超过限制,另一个原因就是在使用完数据库连接后未能即使释放,最后造成数据连接数持续增加从而超出数据库连接池的限制,所以我们也可以从这两个方面来解决这个问题,但是根本上还是得

随机推荐