目录
说明
定时任务调度在很多场景中应用,并且市场上也有很多技术栈(如@Scheduled、ScheduledThreadPoolExecutor、quartz等),下面就以Springboot集成这些定时任务调度技术进行对比,比较在实际应用中的优劣。
一、springboot集成@Scheduled注解
1.1、集成@Scheduled注解方法
要想开启@Scheduled,只需要Application启动类增加@EnableScheduling注解即可。
@SpringBootApplication(scanBasePackages= {
"com.wwy"})
@EnableScheduling //开启定时任务功能
public class LearnitemApplication extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder){
return builder.sources(LearnitemApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(LearnitemApplication.class, args);
}
}
具体使用过程
@Component
public class ScheduleTaskServer {
/**
* cron 每10秒执行
*/
@Scheduled(cron = "0/1 * * * * *")
public void cronV1(){
System.out.println("cronV1 "+Thread.currentThread().getName()+" 执行时间start:"+new Date());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Scheduled(cron = "0/1 * * * * *")
public void cronV2(){
System.out.println("cronV2 "+Thread.currentThread().getName()+" 执行时间start:"+new Date());
}
}
结果:
* cronV2 scheduling-1 执行时间start:Mon Jan 10 11:18:35 CST 2022
* cronV1 scheduling-1 执行时间start:Mon Jan 10 11:18:35 CST 2022
* cronV2 scheduling-1 执行时间start:Mon Jan 10 11:18:40 CST 2022
* cronV1 scheduling-1 执行时间start:Mon Jan 10 11:18:41 CST 2022
* cronV2 scheduling-1 执行时间start:Mon Jan 10 11:18:46 CST 2022
* cronV1 scheduling-1 执行时间start:Mon Jan 10 11:18:47 CST 2022
* cronV2 scheduling-1 执行时间start:Mon Jan 10 11:18:52 CST 2022
* cronV1 scheduling-1 执行时间start:Mon Jan 10 11:18:53 CST 2022
结果分析:
即使开启两个@Scheduled,也是同一个线程【scheduling-1】执行,说明@Scheduled注解是单线程。
本身 cronV2先执行完,然后等待cronV1执行完,才能执行cronV2。
1.2、集成@Scheduled注解优劣点
存在问题:
(1)、单线程执行,如果某个任务阻塞会影响其他任务;
(2)、某个任务失败,后续还会继续执行,并且任务随着系统服务启动而开启,不能终止或中途添加;
(3)、只能适用于单机服务架构,不适合分布式场景;
1.3、集成@Scheduled注解改进
针对上面第一个存在问题,可以使用多线程来解决。--------------采用多线程解决方法有两种:(1)、通过配置设置Scheduled为多线程;(2)、自己维护线程池。
通过配置设置Scheduled为多线程
@Configuration
public class ScheduleConfiguration implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5));
}
}
自己维护线程池
@Component
public class AsyncScheduleTaskServer {
private static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2,2,1000,TimeUnit.SECONDS,new LinkedBlockingDeque<>(100));
@Scheduled(cron = "0/1 * * * * *")
public void asyncCronV1(){
poolExecutor.execute(()->{
System.out.println("async CronV1 "+Thread.currentThread().getName()+" 执行时间start:"+new Date());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
@Scheduled(cron = "0/1 * * * * *")
public void asyncCronV2(){
poolExecutor.execute(()->{
System.out.println("async CronV2 "+Thread.currentThread().getName()