spring scheduled单线程和多线程使用过程中的大坑
作者:程序员大佬 发布时间:2022-09-24 05:51:10
公司在使用定时任务的时候,使用的是spring scheduled。
代码如下:
@EnableScheduling
public class TaskFileScheduleService {
@Scheduled(cron="0 */1 * * * ?")
public void task1(){
.......
}
@Scheduled(cron="0 */1 * * * ?")
public void task2(){
.......
}
某天,接到领导的电话,说生产环境的定时任务不跑了,赶紧给看看~
做为一名负责人的程序员,赶紧放下手中泡面,远程到公司的电脑~
线程卡死这种问题,第一步当然是将jvm中的heap dump和thread dump导出来~
经过简单分析,thread dump中某个线程确实一直处理running状态,heap dump没啥问题~
thread dump中的问题线程:
"pool-2-thread-43" #368 prio=5 os_prio=0 tid=0x00005587fd54c800 nid=0x1df runnable [0x00007ff7e2056000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at java.net.SocketInputStream.read(SocketInputStream.java:224)
at ch.ethz.ssh2.transport.ClientServerHello.readLineRN(ClientServerHello.java:30)
at ch.ethz.ssh2.transport.ClientServerHello.<init>(ClientServerHello.java:67)
at ch.ethz.ssh2.transport.TransportManager.initialize(TransportManager.java:455)
at ch.ethz.ssh2.Connection.connect(Connection.java:643)
- locked <0x000000074539e0e8> (a ch.ethz.ssh2.Connection)
at ch.ethz.ssh2.Connection.connect(Connection.java:490)
- locked <0x000000074539e0e8> (a ch.ethz.ssh2.Connection)
at com.suneee.yige.medicalserver.common.SSHUtils.connect(SSHUtils.java:24)
at com.suneee.yige.medicalserver.service.TaskFileScheduleService.getConn(TaskFileScheduleService.java:102)
at com.suneee.yige.medicalserver.service.TaskFileScheduleService.taskInfo(TaskFileScheduleService.java:108)
at com.suneee.yige.medicalserver.service.TaskFileScheduleService.task(TaskFileScheduleService.java:74)
at sun.reflect.GeneratedMethodAccessor295.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:65)
at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54)
at org.springframework.scheduling.concurrent.ReschedulingRunnable.run(ReschedulingRunnable.java:81)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
很明显,ch.ethz.ssh2.Connection.connect这个方法卡死,导致线程一直处于running状态。
由于spring scheduled默认是所有定时任务都在一个线程中执行!!这是个大坑!!!
也就是说定时任务1一直在执行,定时任务2一直在等待定时任务1执行完成。这就导致了生产上定时任务全部卡死的现象。
问题已经很明确了,要么解决ch.ethz.ssh2.Connection.connect卡死的问题,要么解决spring scheduled单线程处理的问题。
首先,想到的是处理ch.ethz.ssh2.Connection.connect卡死的问题,但是经过一番查找,发现这个ssh的工具包很久没更更新过了,也没有设置例如httpclient的超时时间之类的。这就很难办了!果断放弃!!
现在只剩一条路,怎么在任务1卡死的时候,任务2可以按他自己的周期执行,且任务1也按照固定周期执行,不会因为某次任务1卡死导致后续的定时任务出现问题!
方法一:
添加配置
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(50));
}
}
这个方法,在程序启动后,会逐步启动50个线程,放在线程池中。每个定时任务会占用1个线程。但是相同的定时任务,执行的时候,还是在同一个线程中。
例如,程序启动,每个定时任务占用一个线程。任务1开始执行,任务2也开始执行。如果任务1卡死了,那么下个周期,任务1还是处理卡死状态,任务2可以正常执行。也就是说,任务1某一次卡死了,不会影响其他线程,但是他自己本身这个定时任务会一直等待上一次任务执行完成!
这种显然不行!这也是踩过坑才知道的!!!
方法二(正解):
添加配置:
@Configuration
@EnableAsync
public class ScheduleConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(50);
return taskScheduler;
}
}
在方法上添加注解@Async
@EnableScheduling
public class TaskFileScheduleService {
@Async
@Scheduled(cron="0 */1 * * * ?")
public void task1(){
.......
}
@Async
@Scheduled(cron="0 */1 * * * ?")
public void task2(){
.......
}
这种方法,每次定时任务启动的时候,都会创建一个单独的线程来处理。也就是说同一个定时任务也会启动多个线程处理。
例如:任务1和任务2一起处理,但是线程1卡死了,任务2是可以正常执行的。且下个周期,任务1还是会正常执行,不会因为上一次卡死了,影响任务1。
但是任务1中的卡死线程越来越多,会导致50个线程池占满,还是会影响到定时任务。
这时候,可能会几个月发生一次~到时候再重启就行了!
来源:https://blog.csdn.net/FlyingSnails/article/details/90167434


猜你喜欢
- 复习了下数据结构,用Java的数组实现一下循环队列。队列的类//循环队列class CirQueue{ private int QueueS
- 本文介绍了ListView给每个Item上面的按钮添加事件,具体如下:1.先看下效果图:在这里仅供测试,我把数据都写死了,根据需要可以自己进
- 题目:打印出所有的 "水仙花数 ",所谓 "水仙花数 "是指一个三位数,其各位数字立方和等于该数本身
- Mybatis log printf工具网页地址: http://www.feedme.ltd/log.htmlMybatis执行的sql的
- 虹软免费,高级版本试用支持在线、离线有 Java SDK,C++ SDK一、注册虹软开发者平台点击注册注册完成后可在“我
- 本文实例讲述了C#使用前序遍历、中序遍历和后序遍历打印二叉树的方法。分享给大家供大家参考。具体实现方法如下:public class Bin
- 重写addResourceHandlers映射文件路径在看一个博客源码发现页面的图片所映射的地址在SpringBoot静态资源文件夹下找不到
- 1、官网概括引用官网说法:The Java Virtual Machine defines various run-time data ar
- 目录一、Java 类加载过程1、字节码编译2、加载3、连接4、初始化总结一、Java 类加载过程1、字节码编译编写好 Java 源码 Stu
- springboot 启动项目打印接口列表环境springboot 2.3.2.RELEASE修改配置文件logging: le
- 一、问题描述开发中,需要使Decimal类型数据保留小数点后的两位小数且不需要进行四舍五入操作,即直接截取小数点后面的两位小数即可。例如:1
- 关于java8 的stream排序用法这里不做多说,这里介绍下曾经在多字段排序时遇到过的一个坑。需求:需要根据id去分组,然后取出每组中行号
- @RequestBody与post请求的关系@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的)
- //while和for循环可以相互转换,以下为简单格式;for(1;2;3) A;//等价于
- 大家在使用 Intellij IDEA 的时候会经常遇到各种乱码问题,甚是烦扰。栈长也偶尔会用下IDEA,也有一些解决乱码的经验,我给大家总
- 1、首先,找到 Android SDK 在本机中的位置,如果不知道,可以通过在 Android Studio 找到,如下:2、其次,通过 c
- For-Each循环For-Each循环也叫增强型的for循环,或者叫foreach循环。For-Each循环是JDK5.0的新特性(其他新
- 先看一段代码: private DataSet GetDataSet(string strsql){ string s
- 一、Media FrameWork背景Media Framework (媒体函数库):此函数库让Android 可以播放与录制许多常见的音频
- 这篇文章主要介绍了SpringBoot如何读取war包jar包和Resource资源,文中通过示例代码介绍的非常详细,对大家的学习或者工作具