遇到的问题
看过我之前的文章能发现我从小就有一个并发梦,并且也是对线程协程纤程异步以及并发包有一些了解。这次的接口正好需要多次查询,并发调用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 cloud微服务中,feign开启了熔断器(hystrix):feign.hystrix.enabled=ture,并且使用默认的信号隔离级别,、HttpServletRequest对象在父线程与子线程是相互独立的,不共享的。所以子线程中使用父线程的HttpServletRequest数据为null。
如上,加一句问题确实得到了解决。
RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
三次请求:for循环与线程池加callable的效率差距有一倍。
线程池ThreadPoolExecutor参数设置
解决了调用不通的问题后,就该解决如何设置线程池来提高资源利用性价比。 corePoolSize、maxPoolSize、queueCapacity。 核心线程数、最大线程数、队列容量对线程池是否继续创建线程的影响关系如下:
- 当线程数小于核心线程数时,创建线程。
- 当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。
- 当线程数大于等于核心线程数,且任务队列已满
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
|