问题前提:
1.springTask执行多个定时任务单线程执行(执行完任务A才能执行任务B),导致周期延迟.
2.如果一个定时任务周期是2s,业务执行需要5s,下一次定时周期会阻塞到5s.导致周期延迟
1.创建springBoot项目
2.启动类加入注解@EnableScheduling
@SpringBootApplication
@EnableScheduling
public class application {
public static void main(String[] args) {
SpringApplication.run(application.class,args);
}
}
3.创建定时任务
@Component
public class TestTask {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
// 定义每过3秒执行任务
@Scheduled(fixedRate = 3000)
public void reportCurrentTime() {
System.out.println("任务A:现在时间:" + dateFormat.format(new Date())+Thread.currentThread().getName());
}
// 定义每过3秒执行任务
@Scheduled(fixedRate = 3000)
public void reportCurrentTime2() {
System.out.println("任务B:现在时间:" + dateFormat.format(new Date())+Thread.currentThread().getName());
}
}
执行结果:
任务B:现在时间:15:07:47scheduling-1
任务A:现在时间:15:07:47scheduling-1
任务B:现在时间:15:07:50scheduling-1
任务A:现在时间:15:07:50scheduling-1
任务B:现在时间:15:07:53scheduling-1
任务A:现在时间:15:07:53scheduling-1
任务B:现在时间:15:07:56scheduling-1
任务A:现在时间:15:07:56scheduling-1
4.多个定时任务出现线程阻塞的问题
1.之前有没有发现一个问题(任务A.B使用的是同一线程)?
现在我们修改代码
@Component
public class TestTask {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
// 定义每过3秒执行任务
@Scheduled(fixedRate = 3000)
public void reportCurrentTime() {
System.out.println("任务A:现在时间:" + dateFormat.format(new Date())+Thread.currentThread().getName());
while (true){
try {
//业务执行花费10s
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
// 定义每过3秒执行任务
@Scheduled(fixedRate = 3000)
public void reportCurrentTime2() {
System.out.println("任务B:现在时间:" + dateFormat.format(new Date())+Thread.currentThread().getName());
}
}
执行结果:
任务B:现在时间:15:13:42scheduling-1
任务A:现在时间:15:13:42scheduling-1
任务B:现在时间:15:13:52scheduling-1
任务A:现在时间:15:13:52scheduling-1
任务B:现在时间:15:14:02scheduling-1
任务A:现在时间:15:14:02scheduling-1
任务B:现在时间:15:14:12scheduling-1
任务A:现在时间:15:14:12scheduling-1
任务B:现在时间:15:14:22scheduling-1
任务A:现在时间:15:14:22scheduling-1
线程执行流程: 任务B(阻塞低于1秒)→任务A(阻塞10s)→任务B(低于一秒)→任务A(阻塞10s)
任务A间隔10秒执行(设置周期3s)
任务B间隔10秒执行(设置周期3s)
任务A阻塞会导致B任务出现延迟
解决方法:
加入配置:
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer, AsyncConfigurer {
/** 异步处理 */
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar){
TaskScheduler taskScheduler = taskScheduler();
taskRegistrar.setTaskScheduler(taskScheduler);
}
/** 定时任务多线程处理 */
@Bean(destroyMethod = "shutdown")
public ThreadPoolTaskScheduler taskScheduler(){
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(50);
scheduler.setThreadNamePrefix("task-");
scheduler.setAwaitTerminationSeconds(1);
scheduler.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
/** 异步处理 */
@Override
public Executor getAsyncExecutor(){
Executor executor = taskScheduler();
return executor;
}
/** 异步处理 异常 */
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler(){
return new SimpleAsyncUncaughtExceptionHandler();
}
}
执行结果:
任务A:现在时间:15:23:57task-3
任务B:现在时间:15:23:59task-1
任务B:现在时间:15:24:02task-2
任务B:现在时间:15:24:05task-6
任务A:现在时间:15:24:07task-7
任务B:现在时间:15:24:08task-4
任务B:现在时间:15:24:11task-1
任务B:现在时间:15:24:14task-8
任务B间隔3秒执行 (设置周期3s)
任务A间隔10秒执行 (设置周期3s)
我们暂时解决了多个定时任务之间发生阻塞的问题.
5.单个定时任务如何防止阻塞
1.启动类加入注解支持@EnableAsync
2.在定时任务方法上加入注解@Async
@SpringBootApplication
@EnableScheduling
@EnableAsync
public class application {
public static void main(String[] args) {
SpringApplication.run(application.class,args);
}
}
@Component
public class TestTask {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
// 定义每过3秒执行任务
@Async
@Scheduled(fixedRate = 3000)
public void reportCurrentTime() {
System.out.println("任务A:现在时间:" + dateFormat.format(new Date())+Thread.currentThread().getName());
while (true){
try {
//业务执行花费10s
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
// 定义每过3秒执行任务
@Async
@Scheduled(fixedRate = 3000)
public void reportCurrentTime2() {
System.out.println("任务B:现在时间:" + dateFormat.format(new Date())+Thread.currentThread().getName());
while (true){
try {
//业务执行花费10s
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
break;
}
}
}
执行结果:
任务A:现在时间:15:37:03task-5
任务B:现在时间:15:37:03task-6
任务B:现在时间:15:37:06task-4
任务A:现在时间:15:37:06task-9
任务A:现在时间:15:37:09task-15
任务B:现在时间:15:37:09task-16
任务A:现在时间:15:37:12task-8
任务B:现在时间:15:37:12task-3
任务A间隔3秒执行(设置周期3s)
任务B间隔3秒执行(设置周期3s)
问题解决!
注:ScheduleConfig 配置类中
scheduler.setPoolSize(50); //设置可使用的线程大小