首页 > 其他分享 > 线程池使用InheritableThreadLocal踩坑总结

线程池使用InheritableThreadLocal踩坑总结

时间:2023-08-11 14:12:48浏览次数:39  
标签:总结 请求 代码 线程 new InheritableThreadLocal 页面

一、缘起

某天测试环境更新后,有小伙伴反应页面会随机性的发生请求参数为空的情况(request.getParamter为空),但是前端的参数是传了的,而且不能稳定重现,需要在页面上经过一番操作之后才会发生,而当问题重现之后,之前那些可用的页面就变得不可用了,然后就会在可用和不可用之间交替......

我接到问题的第一反应是

线程池使用InheritableThreadLocal踩坑总结

二、踩坑

2.1 寻找罪魁祸首

代码中request为空,但是前端有传递,第一时间想到的就是线程切换导致ThreadLocal传递出现问题。

然而这个坑我们之前是踩过的,并且已经在切面中手动改成了可继承的线程变量

HttpServletRequest servletRequest = WebUtil.getRequest();
HttpServletResponse servletResponse = WebUtil.getResponse();
//声明子线程的时候,这些属性不会继承,手动赋值成可继承的属性
ServletRequestAttributes attributes = new ServletRequestAttributes(servletRequest, servletResponse);
RequestContextHolder.setRequestAttributes(attributes, true);
LocaleContextHolder.setLocaleContext(LocaleContextHolder.getLocaleContext(), true);

难道切面没生效?

可是经过调试发现,这段代码是进入并执行了的。

通过查看提交记录发现,切面中有人加了这么一段代码(没错就是我)

ExecutorService TIMEOUT_EXECUTOR_POOL = new ThreadPoolExecutor(
    Runtime.getRuntime().availableProcessors() + 1, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
    new SynchronousQueue<>(), ThreadUtil.newNamedThreadFactory("TIMEOUT_EXECUTOR_POOL", false)
);
FutureTask<Object> futureTask = new FutureTask<>(() -> {
    try {
        return joinPoint.proceed();
    } catch (Exception ex) {
        throw ex;
    } catch (Throwable throwable) {
        throw new Exception(throwable);
    }
});
TIMEOUT_EXECUTOR_POOL.submit(futureTask);

为了增加超时时间的控制,我用FutureTask把执行的代码包装了一层

在这里打断点调试,发现在报错的时候,futureTask外部request参数有值,进入后参数为空。

但是,偶尔也是会有值的!有值的时候就是页面正常的时候。

线程池使用InheritableThreadLocal踩坑总结

2.2 找出作案动机(原因)

我们先看下InheritableThreadLocal是怎么实现线程变量可继承的

在Thread的init()方法中有一段代码

private void init(ThreadGroup g, Runnable target, String name,
                  long stackSize, AccessControlContext acc,
                  boolean inheritThreadLocals) {
    //省略部分代码
   
    //如果父线程inheritableThreadLocals不为空,则保存下来
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    
    //省略部分代码
}

可以看到InheritableThreadLocal是在Thread创建的时候继承的。

而我们知道线程池的作用就是“缓存”线程来避免线程频繁的创建和销毁,所以如果在线程池中使用InheritableThreadLocal,只有第一个创建线程时的请求是可以用的,后续请求的InheritableThreadLocal都跟第一个请求一样,不会再改变。

至此,问题原因找到了,因为我创建线程池的时候初始化了CPU核数+1个线程,所以开始一些请求是正常的,后续当这些线程都使用了之后,就会因为InheritableThreadLocal不同导致错误。而且我们自己测试的时候是在几个按钮中重复点击,如果线程的第一个请求是/user/query,当你再次发起这个请求的时候如果刚好分配的是这个线程,页面就是正常的,于是就出现页面时好时坏的情况.

三、填坑

OK,出现问题的地方找到了,下面来解决

1、直接注释掉这段超时控制的代码

这个实在是太粗暴了,只适合紧急情况下使用,作为一个有追求的程序猿,我是不可能这么做的

2、不用线程池,直接new Thread

既然是线程池复用导致的问题,不用线程池就可以解决

3、使用阿里的TransmittableThreadLocal

https://github.com/alibaba/transmittable-thread-local

阿里巴巴开源了一个类似于InheritableThreadLocal的库,就是用来在线程池中使用,有兴趣的可以瞅一眼

转自:http://www.voycn.com/article/xianchengchishiyonginheritablethreadlocalcaikengzongjie

标签:总结,请求,代码,线程,new,InheritableThreadLocal,页面
From: https://www.cnblogs.com/tiancai/p/17622821.html

相关文章

  • 异步线程变量传递必知必会---InheritableThreadLocal及底层原理分析
    InheritableThreadLocal简介笑傲菌:多线程热知识(一):ThreadLocal简介及底层原理3赞同·0评论文章上一篇文章我们聊到了ThreadLocal的作用机理,但是在文章的末尾,我提到了一个问题,ThreadLocal无法实现异步线程变量的传递。什么意思呢?以下面的代码为例子:@SneakyThrowspublicBo......
  • TaskDecorator——异步多线程中传递上下文等变量
    目录TaskDecorator定义TaskDecorator实例线程池使用TaskDecorator开发中很多数据如oauth2的认证信息,日志TracerId都是在请求线程中的,如果内部使用多线程处理就存在获取不到认证信息或TraceId的问题。这时候就需要处理子线程与主线程间数据传递的问题。TaskDecorator这个......
  • 【Elasticsearch】总结
    什么是Elasticsearch?Elasticsearch是基于Lucene的Restful的分布式实时全文搜索引擎,每个字段都被索引并可被搜索,可以快速存储、搜索、分析海量的数据。全文检索是指对每一个词建立一个索引,指明该词在文章中出现的次数和位置。当查询时,根据事先建立的索引进行查找,并将查找的结......
  • CUDA cudaMemcpy函数总结
    在使用cuda的时候一定会用到cudaMemcpy这个函数,因为我们就是用它实现数据在CPU与GPU之间的移动,想在GPU端计算就必须要将数据从CPU拷贝到GPU,想要获得GPU的计算结果就必须将结果拷贝回CPU。但是在使用这个函数的时候对它的第一个参数存在一些疑惑,经过查找资料后做个简单的总结。首......
  • Node+OBS直播服务器搭建总结
    目录直播流媒体协议拉流与推流Node服务搭建前端播放页面OBS推流配置直播流媒体协议先来了解一下基本的直播流媒体协议。拉流与推流推流,指的是把采集阶段封包好的内容传输到服务器的过程。拉流,指服务器已有直播内容,用指定地址进行拉取的过程。Node服务搭建安装......
  • .NET Core多线程 (4) 锁机制
    合集:.NETCore多线程温故知新.NETCore多线程(1)Thread与Task.NETCore多线程(2)异步-上.NETCore多线程(3)异步-下.NETCore多线程(4)锁机制.NETCore多线程(5)常见性能问题 去年换工作时系统复习了一下.NETCore多线程相关专题,学习了一线码农老哥的《.NET5多线程编程实战》......
  • Winform跨线程访问UI
    在开发winfrom应用时,经常遇到异常:System.InvalidOperationException:“线程间操作无效:从不是创建控件“xxxx”的线程访问它。出现这个异常的原因是创建这个UI的线程,和当前访问这个UI的线程不会是同一个。Winform为了防止线程不安全,因此对这个跨线程访问抛出异常,禁止这个操作。......
  • 第七周总结
    下载好linux的远程连接软件Finalshell,开始学习。ls命令:展示当前目录内容ls-a:展示隐藏内容ls-l:展示内容ls-h:展示内容(文件大小)cd命令:切换当前目录cd文件位置pwd命令:查看当前工作目录mkdir命令:创建目录mkdir路径:mkdir-p路径:自动创建路径中没有的文件夹touch命令......
  • CompletableFuture异步多线程
    importjava.util.concurrent.CompletableFuture;importjava.util.concurrent.ExecutionException;publicstaticvoidmain(String[]args)throwsInterruptedException,ExecutionException{longstartTime=System.currentTimeMillis();//调用用户服......
  • 进程 线程 协程的区别
    进程进程是操作系统,进行资源分配和调度的基本单位,多个进程之间相互独立,进程的特点是稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制线程线程是cpu进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的,能独立运......