多线程并发调用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

相关文章

浅析Rust多线程中如何安全的使用变量

《浅析Rust多线程中如何安全的使用变量》这篇文章主要为大家详细介绍了Rust如何在线程的闭包中安全的使用变量,包括共享变量和修改变量,文中的示例代码讲解详细,有需要的小伙伴可以参考下... 目录1. 向线程传递变量2. 多线程共享变量引用3. 多线程中修改变量4. 总结在Rust语言中,一个既引人入胜又可

Python调用另一个py文件并传递参数常见的方法及其应用场景

《Python调用另一个py文件并传递参数常见的方法及其应用场景》:本文主要介绍在Python中调用另一个py文件并传递参数的几种常见方法,包括使用import语句、exec函数、subproce... 目录前言1. 使用import语句1.1 基本用法1.2 导入特定函数1.3 处理文件路径2. 使用ex

Idea调用WebService的关键步骤和注意事项

《Idea调用WebService的关键步骤和注意事项》:本文主要介绍如何在Idea中调用WebService,包括理解WebService的基本概念、获取WSDL文件、阅读和理解WSDL文件、选... 目录前言一、理解WebService的基本概念二、获取WSDL文件三、阅读和理解WSDL文件四、选择对接

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

java如何调用kettle设置变量和参数

《java如何调用kettle设置变量和参数》文章简要介绍了如何在Java中调用Kettle,并重点讨论了变量和参数的区别,以及在Java代码中如何正确设置和使用这些变量,避免覆盖Kettle中已设置... 目录Java调用kettle设置变量和参数java代码中变量会覆盖kettle里面设置的变量总结ja

Spring中Bean有关NullPointerException异常的原因分析

《Spring中Bean有关NullPointerException异常的原因分析》在Spring中使用@Autowired注解注入的bean不能在静态上下文中访问,否则会导致NullPointerE... 目录Spring中Bean有关NullPointerException异常的原因问题描述解决方案总结

如何在页面调用utility bar并传递参数至lwc组件

1.在app的utility item中添加lwc组件: 2.调用utility bar api的方式有两种: 方法一,通过lwc调用: import {LightningElement,api ,wire } from 'lwc';import { publish, MessageContext } from 'lightning/messageService';import Ca

高并发环境中保持幂等性

在高并发环境中保持幂等性是一项重要的挑战。幂等性指的是无论操作执行多少次,其效果都是相同的。确保操作的幂等性可以避免重复执行带来的副作用。以下是一些保持幂等性的常用方法: 唯一标识符: 请求唯一标识:在每次请求中引入唯一标识符(如 UUID 或者生成的唯一 ID),在处理请求时,系统可以检查这个标识符是否已经处理过,如果是,则忽略重复请求。幂等键(Idempotency Key):客户端在每次

多线程解析报表

假如有这样一个需求,当我们需要解析一个Excel里多个sheet的数据时,可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。 Way1 join import java.time.LocalTime;public class Main {public static void main(String[] args) thro

【LabVIEW学习篇 - 21】:DLL与API的调用

文章目录 DLL与API调用DLLAPIDLL的调用 DLL与API调用 LabVIEW虽然已经足够强大,但不同的语言在不同领域都有着自己的优势,为了强强联合,LabVIEW提供了强大的外部程序接口能力,包括DLL、CIN(C语言接口)、ActiveX、.NET、MATLAB等等。通过DLL可以使用户很方便地调用C、C++、C#、VB等编程语言写的程序以及windows自带的大