多线程并发调用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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/weixin_43159092/article/details/123876657
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/677944

相关文章

Java调用C++动态库超详细步骤讲解(附源码)

《Java调用C++动态库超详细步骤讲解(附源码)》C语言因其高效和接近硬件的特性,时常会被用在性能要求较高或者需要直接操作硬件的场合,:本文主要介绍Java调用C++动态库的相关资料,文中通过代... 目录一、直接调用C++库第一步:动态库生成(vs2017+qt5.12.10)第二步:Java调用C++

Feign Client超时时间设置不生效的解决方法

《FeignClient超时时间设置不生效的解决方法》这篇文章主要为大家详细介绍了FeignClient超时时间设置不生效的原因与解决方法,具有一定的的参考价值,希望对大家有一定的帮助... 在使用Feign Client时,可以通过两种方式来设置超时时间:1.针对整个Feign Client设置超时时间

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

使用Sentinel自定义返回和实现区分来源方式

《使用Sentinel自定义返回和实现区分来源方式》:本文主要介绍使用Sentinel自定义返回和实现区分来源方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Sentinel自定义返回和实现区分来源1. 自定义错误返回2. 实现区分来源总结Sentinel自定

在C#中调用Python代码的两种实现方式

《在C#中调用Python代码的两种实现方式》:本文主要介绍在C#中调用Python代码的两种实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#调用python代码的方式1. 使用 Python.NET2. 使用外部进程调用 Python 脚本总结C#调

SpringCloud之LoadBalancer负载均衡服务调用过程

《SpringCloud之LoadBalancer负载均衡服务调用过程》:本文主要介绍SpringCloud之LoadBalancer负载均衡服务调用过程,具有很好的参考价值,希望对大家有所帮助,... 目录前言一、LoadBalancer是什么?二、使用步骤1、启动consul2、客户端加入依赖3、以服务

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

Java使用多线程处理未知任务数的方案介绍

《Java使用多线程处理未知任务数的方案介绍》这篇文章主要为大家详细介绍了Java如何使用多线程实现处理未知任务数,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 知道任务个数,你可以定义好线程数规则,生成线程数去跑代码说明:1.虚拟线程池:使用 Executors.newVir

基于Spring实现自定义错误信息返回详解

《基于Spring实现自定义错误信息返回详解》这篇文章主要为大家详细介绍了如何基于Spring实现自定义错误信息返回效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录背景目标实现产出背景Spring 提供了 @RestConChina编程trollerAdvice 用来实现 HTT