项目是 springboot 写的
用 @Scheduled 写了一个 5 分钟执行一次的定时任务,用 @Async 来做了异步执行
任务开始的时候执行了一个获取系统名称的操作,就没有什么其他操作了
System.getProperty("os.name")
但是发现一个奇怪的问题,定时任务运行一段时间后,总是在晚上九点后就停止执行了
定时任务里面打了日志,也就是说在晚上 9:00 是正常执行完了的 ,但是下一个任务就不再开始执行了
重启 Tomcat 后又正常了
这个就感觉很奇怪。。。。
1
OysterQAQ 2020-10-22 12:51:01 +08:00 via iPhone
cron 默认只有一个线程,加上 async 后交给另一个固定的线程池,所以和 cron 没关系了,排查下那个线程池中各个线程的状态
|
2
echooo0 OP @OysterQAQ 对,是交给了另一个 SimpleAsyncTaskExecutor 的线程池,看日志里面,当时没有任何的报错,感觉不太好排查
|
3
OysterQAQ 2020-10-22 13:15:16 +08:00
@echooo0 可能有别的长时间执行的任务占用了线程池中所有的空闲线程 不过也有可能是别的 cron 任务占用了 cron 的线程 jstack 一下呗
|
4
OysterQAQ 2020-10-22 13:16:16 +08:00
报错是不可能报错的 如果队列大小和拒绝策略有设置的话,也没那么快报错
|
5
echooo0 OP @OysterQAQ 发现了一个问题,线程池的 id 非常大,755 这种
然后查了下,SimpleAsyncTaskExecutor,默认情况下,每次请求都会新开线程,不是真的线程池,这个类不重用线程。 感觉可能和这个有关系,但是开了这么多线程,也没 out of memory 错误报出来,只是 hang 住了,也是很奇怪。。。 |
6
OysterQAQ 2020-10-22 13:25:18 +08:00
@echooo0 哈,,我一般都是对这两个注解指定自己的线程池的 根据业务来新建线程池再在注解中指定使用哪个线程池 不至于互相干扰 我觉得没报错大多是因为根本就在队列中没执行 查查同样用了 cron 的有没有什么耗时很长的大任务
|
7
echooo0 OP @OysterQAQ 你这种 "根据业务来新建线程池再在注解中指定使用哪个线程池" , 可能控制的更精细化一些
我目前修改的办法,是实现了 AsyncConfigurer,定义了全局的线程池都用 ThreadPoolTaskExecutor 来做了 |
8
Vkery 2020-10-22 15:01:00 +08:00
接个 spring boot admin 看看线程状态
我以前遇到过也是定时任务过段时间就停了 后来发现好像是线程池用完了,因为有个定时任务,调用第三方的接口,因为没有超时机制,一直等待在那 |
9
PIECExx 2020-10-22 16:09:13 +08:00
|
10
echooo0 OP @PIECExx 研究了下你说的,在 @Scheduled 里面控制并发数的时候,好像只能设置 corePoolSize,没法设置 maximumPoolSize
```java public class ScheduleConfig implements SchedulingConfigurer { private final Logger logger = LoggerFactory.getLogger(ScheduleConfig.class); @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setScheduler(Executors.newScheduledThreadPool(80)); } } ``` |
11
echooo0 OP 搞定了,实现 ThreadPoolTaskScheduler 即可
|
12
xkzhangsan 2020-10-23 08:18:08 +08:00 via Android
自定义一个线程池比较好
|
13
echooo0 OP |
14
PIECExx 2020-10-23 13:42:51 +08:00
@echooo0 实现异步线程池就是交给另外一个池子做了,只要异步的那个池子没啥问题,任务也不会出错。
configureTasks 这个,你贴出来的 Executors.newScheduledThreadPool(80),是个 new 简版池子的方法。 源码里是 super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); maximumPoolSize 就是 Integer.MAX_VALUE 。 Executors 下面有 N 个方法可以 new 出来,你换个不就行了。。。。 |