多线程并发调用feign,outcome返回NullPointerException

本文主要是介绍多线程并发调用feign,outcome返回NullPointerException,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

遇到的问题

看过我之前的文章能发现我从小就有一个并发梦,并且也是对线程协程纤程异步以及并发包有一些了解。这次的接口正好需要多次查询,并发调用feign请求提上日程。

List<Future<CurveLineDataVO>> futureList = Lists.newArrayList();
for (CurveDTO curveDTO : curveDTOList) {RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);Callable<CurveLineDataVO> callable = () -> getIntervalFlowCurve(curveDTO);futureList.add(pool.submit(callable));
}
for (Future<CurveLineDataVO> future : futureList) {try {curveLineDataVOS.add(future.get());} catch (InterruptedException e) {log.error("future.get()失败", e);} catch (ExecutionException e) {throw new RuntimeException(e.getCause().getMessage());}
}

可是,future.get一直抛出NullPointerException。
调试发现返回的FutTask对象的

state = 3;
outcome = NullPointerException

查找资料得知原因是HttpServletRequest 为null

Spring Boot 默认使用ThreadLocal把Request设置进请求线程中,这样如果在请求方法里面另起一个子线程然后再通过getRequestAttributes方法获取,是获取不到的。
。。。。。。。。。。。。。。。。。。
在Spring cloud微服务中,feign开启了熔断器(hystrix):feign.hystrix.enabled=ture,并且使用默认的信号隔离级别,、HttpServletRequest对象在父线程与子线程是相互独立的,不共享的。所以子线程中使用父线程的HttpServletRequest数据为null。

如上,加一句问题确实得到了解决。

RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);

但是并没有完全解决:用线程池时,新创建的线程能传入token,线程已经存在被再次利用时还是拿不到。这时需要结合threadLocal,Feign的拦截器,来解决问题。

三次请求:for循环与线程池加callable的效率差距有一倍。
并发调用feign

线程池ThreadPoolExecutor参数设置

解决了调用不通的问题后,就该解决如何设置线程池来提高资源利用性价比。
corePoolSize、maxPoolSize、queueCapacity。
核心线程数、最大线程数、队列容量对线程池是否继续创建线程的影响关系如下:

  1. 当线程数小于核心线程数时,创建线程。
  2. 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
  3. 当线程数大于等于核心线程数,且任务队列已满
    1. 若线程数小于最大线程数,创建线程
    2. 若线程数等于最大线程数,抛出异常,拒绝任务
这方面还需要继续学习。以下是这次实践的一些体会。

阿里java开发规范说不要用Executors创建线程池。

Executors.newFixedThreadPool()

由于这个接口调用才会用到线程池,所以将corePoolSize设置为大于0,没任务也会存活,难免觉得浪费资源。
但是当corePoolSize=0时,其他参数怎么设置,效率都上不来,例如:

private static final ExecutorService  pool = new ThreadPoolExecutor(
0, 20, 5L, TimeUnit.SECONDS, 
new LinkedBlockingQueue<>());
原因是

他会将任务加入队列,然后创建一个线程执行,队列不满不会创建更多的线程去执行。导致线程池只有一个线程活跃。
线程池
如图,只有thread1.
于是,我想着,若是队列满了会怎样呢,把队列的容量设置为1

private static final ExecutorService  pool = new ThreadPoolExecutor(
0, 20, 5L, TimeUnit.SECONDS, 
new LinkedBlockingQueue<>(1));

执行线程确实多了,效率提升明显。
执行线程
但是这样的设置也有一个致命的缺陷。
就是当maxPoolSize太小了,任务数量太多,就会被拒绝而报错。

java.util.concurrent.RejectedExecutionException
Task java.util.concurrent.FutureTask@53b8ecad rejected from java.util.concurrent.ThreadPoolExecutor@1ad994af

allowCoreThreadTimeout

还有一个allowCoreThreadTimeout参数,或许能达到我想让线程池不断给我创建新的线程去执行,同时在空闲时也不占用资源。
allowCoreThreadTimeout = true
能让核心线程在超时后退出,直到核心线程数量=0

最后决定

private static final ThreadPoolExecutor pool;static {pool = new ThreadPoolExecutor(3, 5, 5L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());pool.allowCoreThreadTimeOut(true);
}

这篇关于多线程并发调用feign,outcome返回NullPointerException的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/677944

相关文章

Java JUC并发集合详解之线程安全容器完全攻略

《JavaJUC并发集合详解之线程安全容器完全攻略》Java通过java.util.concurrent(JUC)包提供了一整套线程安全的并发容器,它们不仅是简单的同步包装,更是基于精妙并发算法构建... 目录一、为什么需要JUC并发集合?二、核心并发集合分类与详解三、选型指南:如何选择合适的并发容器?在多

Java 结构化并发Structured Concurrency实践举例

《Java结构化并发StructuredConcurrency实践举例》Java21结构化并发通过作用域和任务句柄统一管理并发生命周期,解决线程泄漏与任务追踪问题,提升代码安全性和可观测性,其核心... 目录一、结构化并发的核心概念与设计目标二、结构化并发的核心组件(一)作用域(Scopes)(二)任务句柄

MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决

《MyBatis/MyBatis-Plus同事务循环调用存储过程获取主键重复问题分析及解决》MyBatis默认开启一级缓存,同一事务中循环调用查询方法时会重复使用缓存数据,导致获取的序列主键值均为1,... 目录问题原因解决办法如果是存储过程总结问题myBATis有如下代码获取序列作为主键IdMappe

使用Go调用第三方API的方法详解

《使用Go调用第三方API的方法详解》在现代应用开发中,调用第三方API是非常常见的场景,比如获取天气预报、翻译文本、发送短信等,Go作为一门高效并发的编程语言,拥有强大的标准库和丰富的第三方库,可以... 目录引言一、准备工作二、案例1:调用天气查询 API1. 注册并获取 API Key2. 代码实现3

SpringBoot分段处理List集合多线程批量插入数据方式

《SpringBoot分段处理List集合多线程批量插入数据方式》文章介绍如何处理大数据量List批量插入数据库的优化方案:通过拆分List并分配独立线程处理,结合Spring线程池与异步方法提升效率... 目录项目场景解决方案1.实体类2.Mapper3.spring容器注入线程池bejsan对象4.创建

Web服务器-Nginx-高并发问题

《Web服务器-Nginx-高并发问题》Nginx通过事件驱动、I/O多路复用和异步非阻塞技术高效处理高并发,结合动静分离和限流策略,提升性能与稳定性... 目录前言一、架构1. 原生多进程架构2. 事件驱动模型3. IO多路复用4. 异步非阻塞 I/O5. Nginx高并发配置实战二、动静分离1. 职责2

Python多线程实现大文件快速下载的代码实现

《Python多线程实现大文件快速下载的代码实现》在互联网时代,文件下载是日常操作之一,尤其是大文件,然而,网络条件不稳定或带宽有限时,下载速度会变得很慢,本文将介绍如何使用Python实现多线程下载... 目录引言一、多线程下载原理二、python实现多线程下载代码说明:三、实战案例四、注意事项五、总结引

Java调用Python脚本实现HelloWorld的示例详解

《Java调用Python脚本实现HelloWorld的示例详解》作为程序员,我们经常会遇到需要在Java项目中调用Python脚本的场景,下面我们来看看如何从基础到进阶,一步步实现Java与Pyth... 目录一、环境准备二、基础调用:使用 Runtime.exec()2.1 实现步骤2.2 代码解析三、

Spring Security 前后端分离场景下的会话并发管理

《SpringSecurity前后端分离场景下的会话并发管理》本文介绍了在前后端分离架构下实现SpringSecurity会话并发管理的问题,传统Web开发中只需简单配置sessionManage... 目录背景分析传统 web 开发中的 sessionManagement 入口ConcurrentSess

Python多线程应用中的卡死问题优化方案指南

《Python多线程应用中的卡死问题优化方案指南》在利用Python语言开发某查询软件时,遇到了点击搜索按钮后软件卡死的问题,本文将简单分析一下出现的原因以及对应的优化方案,希望对大家有所帮助... 目录问题描述优化方案1. 网络请求优化2. 多线程架构优化3. 全局异常处理4. 配置管理优化优化效果1.