记两个有关线程池的小问题

简介: 最近小伙伴们找我查的问题里,有两个与线程池相关的,最终都是花了一些时间才揪出原因所在,做一下记录。

最近小伙伴们找我查的问题里,有两个与线程池相关的,最终都是花了一些时间才揪出原因所在,做一下记录,供以后的自己和其它需要的人参考。

一、异步变同步

现象:

有一个方法,被请求后只是向线程池提交一个任务,然后马上返回,但从日志的 traceId 来看,偶现方法与任务在同一线程执行,接口耗时较长的情况。

分析过程:

这个其实就是一个知识点:当线程池里没有空闲线程,且任务队列已满时,会怎么处理新提交的任务?

可以看下 TheadPoolExecutor 类,这个类里面有几种预定义好的策略(implements RejectedExecutionHandler):

  • CallerRunsPolicy

  • AbortPolicy

  • DiscardPolicy

  • DiscardOldestPolicy

结合它们的名字以及注释就可以看到,它们分别对应:

  • 调度线程自己执行任务;(有一种例外情况是线程池被 shutdown 了则丢弃任务)

  • 忽略任务,并抛出异常;(默认值)

  • 丢弃任务,不产生异常;

  • 丢弃队列里最老的未被处理的任务,然后重新尝试调度新任务;(例外情况同一)

除此之外,还可以按需自己定义策略。

在我们的场景里,这个线程池使用的 RejectedExecutionHandler 是 CallerRunsPolicy,所以原因就找到了。

解决方案:

因为场景里主要的诉求是这个接口要快速返回,并且不能丢失任务,那这种情况使用消息队列会更加合适,所以将这里的向线程池提交任务,修改为向消息队列发送消息。

二、消失的任务

现象:

从日志可以看到,向线程池里提交了一个任务,找不到该任务执行的记录。

分析过程:

首先是怀疑这个任务被丢弃或者忽略了,经确认,该线程池的 RejectExecutionHandler 是使用的默认的 AbortPolicy,这样的话如果它被忽略,会有异常抛出,但日志里找不到异常记录。

那就是说,它成功进入了任务队列,但是没有被执行,哪里去了呢?

冥思苦想之后,怀疑是不是应用被杀掉了?查看 K8s 控制台里容器的滚动记录,果然在提交任务的时间点附近,应用发过版——破案。

解决方案:

提供两个思路:

  • 在保证任务执行逻辑幂等的前提下,通过消息队列、数据库记录任务状态+重试机制等方式调度任务;

  • 容器优雅下线,确认正在处理的请求和任务都完成后才能被 kill 掉。

目录
相关文章
|
缓存 Java 应用服务中间件
线程池的10个坑你都遇到过吗
日常开发中,为了更好管理线程资源,减少创建线程和销毁线程的资源损耗,我们会使用线程池来执行一些异步任务。但是线程池使用不当,就可能会引发生产事故。大家看完肯定会有帮助的~
246 0
|
4月前
|
Java 调度
基于C++11的线程池
基于C++11的线程池
|
5月前
|
缓存 Java
线程池使用小结
线程池使用小结
33 0
|
7月前
|
存储 Java 调度
浅谈线程池
浅谈线程池
45 1
|
缓存 Java
线程池简单总结
线程池简单总结
|
Java
6. 实现简单的线程池
6. 实现简单的线程池
60 0
|
缓存 算法 Java
线程池和使用
线程池是一种用于管理和复用线程的机制。在多线程应用程序中,线程的创建和销毁需要消耗大量的系统资源,而线程池可以通过预先创建一定数量的线程,然后将任务分配给这些线程来避免频繁地创建和销毁线程,从而提高应用程序的性能和效率。线程池还可以控制并发线程的数量,避免过多的线程竞争资源导致的性能下降和系统崩溃。线程池是多线程编程中常用的一种技术,被广泛应用于各种类型的应用程序中。
81 0
线程池和使用
|
前端开发 Java 调度
线程池的使用
线程池的使用
|
Java 数据库连接 容器
关于线程池
关于线程池
104 0
|
存储 Java 调度
线程池使用
线程池使用