最近小伙伴们找我查的问题里,有两个与线程池相关的,最终都是花了一些时间才揪出原因所在,做一下记录,供以后的自己和其它需要的人参考。
现象:
有一个方法,被请求后只是向线程池提交一个任务,然后马上返回,但从日志的 traceId 来看,偶现方法与任务在同一线程执行,接口耗时较长的情况。
分析过程:
这个其实就是一个知识点:当线程池里没有空闲线程,且任务队列已满时,会怎么处理新提交的任务?
可以看下 TheadPoolExecutor 类,这个类里面有几种预定义好的策略(implements RejectedExecutionHandler):
结合它们的名字以及注释就可以看到,它们分别对应:
除此之外,还可以按需自己定义策略。
在我们的场景里,这个线程池使用的 RejectedExecutionHandler 是 CallerRunsPolicy,所以原因就找到了。
解决方案:
因为场景里主要的诉求是这个接口要快速返回,并且不能丢失任务,那这种情况使用消息队列会更加合适,所以将这里的向线程池提交任务,修改为向消息队列发送消息。
现象:
从日志可以看到,向线程池里提交了一个任务,找不到该任务执行的记录。
分析过程:
首先是怀疑这个任务被丢弃或者忽略了,经确认,该线程池的 RejectExecutionHandler 是使用的默认的 AbortPolicy,这样的话如果它被忽略,会有异常抛出,但日志里找不到异常记录。
那就是说,它成功进入了任务队列,但是没有被执行,哪里去了呢?
冥思苦想之后,怀疑是不是应用被杀掉了?查看 K8s 控制台里容器的滚动记录,果然在提交任务的时间点附近,应用发过版——破案。
解决方案:
提供两个思路: