使用线程池仍同步处理请求问题
发现:
线上一个使用线程池处理任务的RPC接口超时:
@Resource
private ThreadPoolTaskExecutor threadPool;
@Override
public Response rpcMethod(Request request) throws TException {
//这里使用匿名内部类方式创建Runnable类型的线程
threadPool.submit(() -> myMethod(request));
}
排查
上面接口在日志里显示耗时3s, 按理收到请求后就使用线程池中的其他线程处理了,没有等待返回结果,接口应该很快,这里并没有使用Callable类型获取Future结果,所以理论上不会发生阻塞。
排查trace日志,发现显示这次整个调用链将myMethod内部的调用都打印出来,时间也都算在rpcMethod内了,说明线程池没有起作用,同步处理的这次请求
原因
查看线程池配置如下:
<!-- 多线程配置 -->
<bean id="threadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<!-- 线程池的前缀 -->
<property name="threadNamePrefix" value="myThread"/>
<!-- 核心线程数 -->
<property name="corePoolSize" value="5" />
<!-- 最大线程数 -->
<property name="maxPoolSize" value="10" />
<!-- 队列最大长度-->
<property name="queueCapacity" value="500" />
<!-- 线程池维护线程所允许的空闲时间,默认为60s -->
<property name="keepAliveSeconds" value="30" />
<!-- 核心线程超时退出,默认为false -->
<property name="allowCoreThreadTimeOut" value="true" />
<!-- 线程池对拒绝任务(无线程可用)的处理策略,目前只支持AbortPolicy、CallerRunsPolicy;默认为后者 -->
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy" />
</property>
</bean>
当时的并发请求量为1000,在线程池中的处理为:
-
如果运行的线程少于corePoolSize-5,则创建新线程来处理任务,即使线程池中的其他线程是空闲的 (这里前5个请求被放进阻塞队列中)
-
如果线程池中的线程数量大于等于corePoolSize-5 且小于 maximumPoolSize-10,则只有当workQueue-500 满时才创建新的线程去处理任务。 (这里同时到达的第6-506个请求被放进阻塞队列中)
-
如果设置的corePoolSize 和 maximumPoolSize相同,则创建的线程池的大小是固定的,这时如果有新任务提交,若workQueue未满,则将请求放入workQueue中,等待有空闲的线程去从workQueue中取任务并处理;
-
如果workQueue已经满了,会创建新线程处理新请求 (这里第507个到512个请求新建了5个线程处理) 直到线程数到达maximumPoolSize-10, 512后面的请求,由于最大线程数和阻塞队列都有任务了,则通过handler所指定的策略来处理任务
线程池任务的优先级:corePoolSize>queueCapacity>maxPoolSize
配置里制定的策略为CallerRunsPolicy:使用线程调用者所在的线程来执行任务
线程池提供了4种策略: -
AbortPolicy:直接抛出异常,这是默认策略;
-
CallerRunsPolicy:用调用者所在的线程来执行任务;
-
DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
-
DiscardPolicy:直接丢弃任务;
参考:
ThreadPoolExecutor里面4种拒绝策略–CallerRunsPolicy
Java多线程-线程池ThreadPoolExecutor的submit返回值Future
本文地址:https://blog.csdn.net/weixin_42655186/article/details/107359954