Java中定时任务ScheduledThreadPoolExecutor、Timer、@Scheduled和Quartz
1.简介
在实际应用中,有时候我们需要创建一些个延迟的、并具有周期性的任务,比如,我们希望当我们的程序启动后每隔1小时就去做一次日志记录、每天凌晨12点去清理一下数据库中的过期数据等。在JDK中提供了两种方法去创建延迟周期性任务。分别是ScheduledThreadPoolExecutor和Timer。另外还有一个开源的更加强大的任务调度框架Quartz。下面我们来具体认识这三个框架。
2.Timer
Timer的主要方法有:
// 安排在指定的时间执行
void schedule(TimerTask task, Date time);
// 安排在指定的时间开始以重复的延时执行
void schedule(TimerTask task, Date firstTime, long period);
// 安排在指定的延迟后执行
void schedule(TimerTask task, long delay);
// 安排在指定的延迟后开始以重复的延时执行
void schedule(TimerTask task, long delay, long period);
// 安排在指定的时间开始以重复的速率执行
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period);
// 安排在指定的延迟后开始以重复的速率执行
void scheduleAtFixedRate(TimerTask task, long delay, long period);
//可以在任何时刻调用cancel方法终止timer线程
cancel();
//从任务队列中删除已取消的任务,返回删除的数量
int purge();
注:重复的延时和重复的速率的区别在于,前者是在前一个任务的执行结束后间隔period时间再开始下一次执行;而scheduleAtFixedRate则是会尽量按照任务的初始时间来按照间隔period时间执行。如果一次任务执行由于某些原因被延迟了,用schedule()调度的后续任务同样也会被延迟,而用scheduleAtFixedRate()则会快速的开始两次或者多次执行,是后续任务的执行时间能够赶上来。
3.ScheduledThreadPoolExecutor
**ScheduledThreadPoolExecutor的主要方法:
// 在指定的延迟后执行
<V> ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
// 在指定的延迟后执行
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
// 在指定的延迟后以固定速率执行
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
// 在指定的延迟后以固定间隔执行
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
关于ScheduledThreadPoolExecutor,我在线程池的文章中已经详细描述过,大家可以参考Java中的线程池
如果是想使用JDK中的定时任务,那么推荐使用ScheduledThreadPoolExecutor而不是Timer。
4.Spring中定时任务@Scheduled
Spring Task不是独立的项目,是spring-context 模块下提供的定时任务工具,是Spring3.0以后自带的task,可以将它看成一个轻量级的Quartz。
使用Spring Task有两种方式,一种是基于注解****@Scheduled,一种是基于配置文件。在springboot中推荐使用注解和配置类的方式,这里我们主要使用注解和配置类,基于配置文件的也会给出demo。
基于注解
在springboot的启动类上通过注解@EnableScheduling开启。然后在类的方法上通过@Scheduled注解使用,代码案例如下:
@Component
public class ScheduleTest {
@Scheduled(fixedDelayString = "5000")
public void testFixedDelayString() {
System.out.println("Execute at " + System.currentTimeMillis());
}
}
基于注解的使用可以参考我之前的文章,很详细:@scheduled注解的使用
基于xml配置
首先是任务类:
public class SpringTask {
private SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void m1(){
System.out.println("m1:"+simpleDateFormat.format(new Date()));
}
public void m2(){
System.out.println("m2:"+simpleDateFormat.format(new Date()));
}
public void m3(){
System.out.println("m2:"+simpleDateFormat.format(new Date()));
}
}
然后是xml配置:
<!--spring-task.xml配置-->
<bean id="springTask" class="com.njit.springtask.SpringTask"></bean>
<!--注册调度任务-->
<task:scheduled-tasks>
<!--延迟8秒 执行任务-->
<!--<task:scheduled ref="springTask" method="m1" fixed-delay="8000" />-->
<!--固定速度5秒 执行任务-->
<!--<task:scheduled ref="springTask" method="m2" fixed-rate="5000"/>-->
<!--
使用cron表达式 指定触发时间
spring task 只支持6位的cron表达式 秒 分 时 日 月 星期
-->
<task:scheduled ref="springTask" method="m3" cron="50-59 * * ? * *"/>
</task:scheduled-tasks>
<!--执行器配置-->
<task:executor id="threadPoolTaskExecutor" pool-size="10" keep-alive="5"></task:executor>
<!--调度器配置-->
<task:scheduler id="threadPoolTaskScheduler" pool-size="10"></task:scheduler>
Spring Task 任务执行器以及调度器解释
实现原理
spring task对定时任务的两个抽象:
TaskExecutor:与jdk中Executor相同,引入的目的是为定时任务的执行提供线程池的支持,如果不设置**,默认只有一个线程。**
TaskScheduler:提供定时任务支持,需要传入一个Runnable的任务做为参数,并指定需要周期执行的时间或者触发器,这样Runnable任务就可以周期性执行了。
继承关系如下:
任务执行器与调度器的实现类分别为ThreadPoolTaskExecutor、ThreadPoolTaskScheduler
TaskScheduler需要传入一个Runnable的任务做为参数,并指定需要周期执行的时间或者触发器(Trigger)。
spring定义了Trigger接口的实现类CronTrigger,支持使用cron表达式指定定时策略,使用如下:
scheduler.schedule(task, new CronTrigger("30 * * * * ?"));
在springboot项目中,我们一般都是使用@schedule注解来使用spring task,这个注解内部的实现就是使用上面的内容。
spring在初始化bean后,通过postProcessAfterInitialization拦截到所有的用到**@Scheduled**注解的方法,并解析相应的的注解参数,放入“定时任务列表”等待后续处理;之后再“定时任务列表”中统一执行相应的定时任务(任务为顺序执行,先执行cron,之后再执行fixedRate)
具体的源码解析可参考:Spring中的Scheduling模块
@Schedule注解源码解析
总结
定时任务@Scheduled之单线程多线程问题
SpringBoot使用@scheduled定时执行任务的时候是在一个单线程中,如果有多个任务,其中一个任务执行时间过长,则有可能会导致其他后续任务被阻塞直到该任务执行完成。也就是会造成一些任务无法定时执行的错觉。
可以通过如下代码进行测试:
@Scheduled(cron = "0/1 * * * * ? ")
public void deleteFile() throws InterruptedException {
log.info("111delete success, time:" + new Date().toString());
Thread.sleep(1000 * 5);//模拟长时间执行,比如IO操作,http请求
}
@Scheduled(cron = "0/1 * * * * ? ")
public void syncFile() {
log.info("222sync success, time:" + new Date().toString());
}
2021-08-25 23:38:27.008 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:38:27 CST 2021
2021-08-25 23:38:32.010 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:38:32 CST 2021
2021-08-25 23:38:33.004 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:38:33 CST 2021
2021-08-25 23:38:38.007 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:38:38 CST 2021
2021-08-25 23:38:39.007 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:38:39 CST 2021
2021-08-25 23:38:44.014 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:38:44 CST 2021
2021-08-25 23:38:45.015 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:38:45 CST 2021
2021-08-25 23:38:50.027 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:38:50 CST 2021
2021-08-25 23:38:51.001 INFO 18972 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:38:51 CST 2021
上面的日志中可以明显的看到syncFile被阻塞了,直达deleteFile执行完它才执行了
而且从日志信息中也可以看出@Scheduled是使用了一个线程池中的一个单线程来执行所有任务的。
/**如果把Thread.sleep(1000*5)注释了,输出如下:
2021-08-25 23:40:12.003 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:40:12 CST 2021
2021-08-25 23:40:12.004 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:40:12 CST 2021
2021-08-25 23:40:13.015 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:40:13 CST 2021
2021-08-25 23:40:13.015 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:40:13 CST 2021
2021-08-25 23:40:14.015 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:40:14 CST 2021
2021-08-25 23:40:14.015 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:40:14 CST 2021
2021-08-25 23:40:15.011 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:40:15 CST 2021
2021-08-25 23:40:15.011 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:40:15 CST 2021
2021-08-25 23:40:16.005 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:40:16 CST 2021
2021-08-25 23:40:16.005 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:40:16 CST 2021
2021-08-25 23:40:17.002 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 111delete success, time:Wed Aug 25 23:40:17 CST 2021
2021-08-25 23:40:17.002 INFO 17988 --- [ scheduling-1] com.njit.springtask.ScheduleTest : 222sync success, time:Wed Aug 25 23:40:17 CST 2021
查看源码,从ScheduledAnnotationBeanPostProcessor类开始一路找下去。果然,在ScheduledTaskRegistrar(定时任务注册类)中的ScheduleTasks中又这样一段判断:
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
这就说明如果taskScheduler为空,那么就给定时任务做了一个单线程的线程池,正好在这个类中还有一个设置taskScheduler的方法:
public void setScheduler(Object scheduler) {
Assert.notNull(scheduler, "Scheduler object must not be null");
if (scheduler instanceof TaskScheduler) {
this.taskScheduler = (TaskScheduler) scheduler;
}
else if (scheduler instanceof ScheduledExecutorService) {
this.taskScheduler = new ConcurrentTaskScheduler(((ScheduledExecutorService) scheduler));
}
else {
throw new IllegalArgumentException("Unsupported scheduler type: " + scheduler.getClass());
}
}
解决办法
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某一次卡死了,不会影响其他线程,但是他自己本身这个定时任务会一直等待上一次任务执行完成!
按照上面配置后,启动任务
@Component
@Slf4j
public class ScheduleTest {
@Scheduled(cron = "0/1 * * * * ? ")
public void deleteFile() throws InterruptedException {
log.info(Thread.currentThread().getName()+" 线程 1111111delete success, time:" + new Date().toString());
Thread.sleep(1000 * 10);//模拟长时间执行,比如IO操作,http请求
}
@Scheduled(cron = "0/1 * * * * ? ")
public void syncFile() throws InterruptedException {
log.info(Thread.currentThread().getName()+" 线程 22222222sync success, time:" + new Date().toString());
Thread.sleep(1000 * 10);//模拟长时间执行,比如IO操作,http请求
}
}
日志如下:
2021-08-26 22:54:58.006 INFO 1776 --- [pool-1-thread-2] com.njit.springtask.ScheduleTest : pool-1-thread-2 线程 1111111delete success, time:Thu Aug 26 22:54:58 CST 2021
2021-08-26 22:54:58.006 INFO 1776 --- [pool-1-thread-1] com.njit.springtask.ScheduleTest : pool-1-thread-1 线程 22222222sync success, time:Thu Aug 26 22:54:58 CST 2021
2021-08-26 22:55:09.003 INFO 1776 --- [pool-1-thread-1] com.njit.springtask.ScheduleTest : pool-1-thread-1 线程 22222222sync success, time:Thu Aug 26 22:55:09 CST 2021
2021-08-26 22:55:09.003 INFO 1776 --- [pool-1-thread-2] com.njit.springtask.ScheduleTest : pool-1-thread-2 线程 1111111delete success, time:Thu Aug 26 22:55:09 CST 2021
2021-08-26 22:55:20.005 INFO 1776 --- [pool-1-thread-3] com.njit.springtask.ScheduleTest : pool-1-thread-3 线程 1111111delete success, time:Thu Aug 26 22:55:20 CST 2021
2021-08-26 22:55:20.005 INFO 1776 --- [pool-1-thread-4] com.njit.springtask.ScheduleTest : pool-1-thread-4 线程 22222222sync success, time:Thu Aug 26 22:55:20 CST 2021
2021-08-26 22:55:31.004 INFO 1776 --- [pool-1-thread-2] com.njit.springtask.ScheduleTest : pool-1-thread-2 线程 1111111delete success, time:Thu Aug 26 22:55:31 CST 2021
2021-08-26 22:55:31.004 INFO 1776 --- [pool-1-thread-1] com.njit.springtask.ScheduleTest : pool-1-thread-1 线程 22222222sync success, time:Thu Aug 26 22:55:31 CST 2021
2021-08-26 22:55:42.011 INFO 1776 --- [pool-1-thread-3] com.njit.springtask.ScheduleTest : pool-1-thread-3 线程 1111111delete success, time:Thu Aug 26 22:55:42 CST 2021
2021-08-26 22:55:42.011 INFO 1776 --- [pool-1-thread-6] com.njit.springtask.ScheduleTest : pool-1-thread-6 线程 22222222sync success, time:Thu Aug 26 22:55:42 CST 2021
2021-08-26 22:55:53.002 INFO 1776 --- [pool-1-thread-4] com.njit.springtask.ScheduleTest : pool-1-thread-4 线程 1111111delete success, time:Thu Aug 26 22:55:53 CST 2021
2021-08-26 22:55:53.002 INFO 1776 --- [pool-1-thread-7] com.njit.springtask.ScheduleTest : pool-1-thread-7 线程 22222222sync success, time:Thu Aug 26 22:55:53 CST 2021
2021-08-26 22:56:04.002 INFO 1776 --- [pool-1-thread-8] com.njit.springtask.ScheduleTest : pool-1-thread-8 线程 22222222sync success, time:Thu Aug 26 22:56:04 CST 2021
2021-08-26 22:56:04.002 INFO 1776 --- [pool-1-thread-5] com.njit.springtask.ScheduleTest : pool-1-thread-5 线程 1111111delete success, time:Thu Aug 26 22:56:04 CST 2021
2021-08-26 22:56:15.006 INFO 1776 --- [pool-1-thread-1] com.njit.springtask.ScheduleTest : pool-1-thread-1 线程 1111111delete success, time:Thu Aug 26 22:56:15 CST 2021
2021-08-26 22:56:15.006 INFO 1776 --- [pool-1-thread-2] com.njit.springtask.ScheduleTest : pool-1-thread-2 线程 22222222sync success, time:Thu Aug 26 22:56:15 CST 2021
2021-08-26 22:56:26.001 INFO 1776 --- [pool-1-thread-9] com.njit.springtask.ScheduleTest : pool-1-thread-9 线程 1111111delete success, time:Thu Aug 26 22:56:26 CST 2021
2021-08-26 22:56:26.001 INFO 1776 --- [ool-1-thread-10] com.njit.springtask.ScheduleTest : pool-1-thread-10 线程 22222222sync success, time:Thu Aug 26 22:56:26 CST 2021
2021-08-26 22:56:37.016 INFO 1776 --- [pool-1-thread-6] com.njit.springtask.ScheduleTest : pool-1-thread-6 线程 22222222sync success, time:Thu Aug 26 22:56:37 CST 2021
2021-08-26 22:56:37.016 INFO 1776 --- [pool-1-thread-3] com.njit.springtask.ScheduleTest : pool-1-thread-3 线程 1111111delete success, time:Thu Aug 26 22:56:37 CST 2021
2021-08-26 22:56:48.014 INFO 1776 --- [ool-1-thread-11] com.njit.springtask.ScheduleTest : pool-1-thread-11 线程 1111111delete success, time:Thu Aug 26 22:56:48 CST 2021
2、把Scheduled配置成多线程执行
@Configuration
public class ScheduleConfig {
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(50);
return taskScheduler;
}
}
@EnableScheduling
public class TaskFileScheduleService {
@Scheduled(cron="0 */1 * * * ?")
public void task1(){
.......
}
@Scheduled(cron="0 */1 * * * ?")
public void task2(){
.......
}
这种方法,程序启动,每个定时任务占用一个线程。任务1开始执行,任务2也开始执行。如果任务1卡死了,那么下个周期,任务1还是处理卡死状态,任务2可以正常执行。也就是说,任务1某一次卡死了,不会影响其他线程,但是他自己本身这个定时任务会一直等待上一次任务执行完成!这种方案和第一种的表现一样!
3、使用@Async注解
@Configuration
@EnableAsync
public class ScheduleConfig {
}
@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中的卡死线程越来越多,最终会把线程池占满,还是会影响到定时任务。(@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor。默认核心线程数:8,最大线程数:Integet.MAX_VALUE,队列使用LinkedBlockingQueue,容量是:Integet.MAX_VALUE,空闲线程保留时间:60s,线程池拒绝策略:AbortPolicy。)
可以通过如下配置@Async的线程池参数:
#核心线程数
spring.task.execution.pool.core-size=200
#最大线程数
spring.task.execution.pool.max-size=1000
#空闲线程保留时间
spring.task.execution.pool.keep-alive=3s
#队列容量
spring.task.execution.pool.queue-capacity=1000
#线程名称前缀
spring.task.execution.thread-name-prefix=test-thread-
4、将@Scheduled注释的方法内部改成异步执行
//当然了,构建一个合理的线程池也是一个关键,否则提交的任务也会在自己构建的线程池中阻塞
ExecutorService service = Executors.newFixedThreadPool(5);
@Scheduled(cron = "0/1 * * * * ? ")
public void deleteFile() {
service.execute(() -> {
log.info("111delete success, time:" + new Date().toString());
try {
Thread.sleep(1000 * 5);//改成异步执行后,就算你再耗时也不会印象到后续任务的定时调度了
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
@Scheduled(cron = "0/1 * * * * ? ")
public void syncFile() {
service.execute(()->{
log.info("222sync success, time:" + new Date().toString());
});
}
5.Quartz
首先我们要了解一下quartz中的一些基本概念:
Scheduler:任务调度器,是实际执行任务调度的控制器。在spring中通过SchedulerFactoryBean封装起来。
Trigger:触发器,用于定义任务调度的时间规则,有SimpleTrigger,CronTrigger,DateIntervalTrigger等,其中CronTrigger用的比较多,本文主要介绍这种方式。CronTrigger在spring中封装在CronTriggerFactoryBean中。
SimpleTrigger:简单触发器,从某个时间开始,每隔多少时间触发,重复多少次。
CronTrigger:使用cron表达式定义触发的时间规则,如"0 0 0,2,4 1/1 * ? *" 表示每天的0,2,4点触发。
DailyTimeIntervalTrigger:每天中的一个时间段,每N个时间单元触发,时间单元可以是毫秒,秒,分,小时
CalendarIntervalTrigger:每N个时间单元触发,时间单元可以是毫秒,秒,分,小时,日,月,年。
Calendar:它是一些日历特定时间点的集合。一个trigger可以包含多个Calendar,以便排除或包含某些时间点。
JobDetail:用来描述Job实现类及其它相关的静态信息,如Job名字、关联监听器等信息。在spring中有JobDetailFactoryBean和 MethodInvokingJobDetailFactoryBean两种实现,如果任务调度只需要执行某个类的某个方法,就可以通过MethodInvokingJobDetailFactoryBean来调用。
Job:是一个接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义运行任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。实现Job接口的任务,默认是无状态的,若要将Job设置成有状态的(即是否支持并发),在quartz中是给实现的Job添加@DisallowConcurrentExecution注解
Quartz 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器。
在 Quartz 中,trigger 是用于定义调度时间的元素,即按照什么时间规则去执行任务。Quartz 中主要提供了四种类型的 trigger:SimpleTrigger,CronTirgger,DailyTimeIntervalTrigger,和 CalendarIntervalTrigger
在 Quartz 中,job 用于表示被调度的任务。主要有两种类型的 job:无状态的(stateless)和有状态的(stateful)。对于同一个 trigger 来说,有状态的 job 不能被并行执行,只有上一次触发的任务被执行完之后,才能触发下一次执行。Job 主要有两种属性:volatility 和 durability,其中 volatility 表示任务是否被持久化到数据库存储,而 durability 表示在没有 trigger 关联的时候任务是否被保留。两者都是在值为 true 的时候任务被持久化或保留。一个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job
使用
引入starter依赖
<!-- quartz -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
编写两个任务Task
/**
* @author
* 任务一
*/
public class TestTask1 extends QuartzJobBean{
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("TestQuartz01----" + sdf.format(new Date()));
}
}
/**
* 任务二
* @author
*/
public class TestTask2 extends QuartzJobBean{
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("TestQuartz02----" + sdf.format(new Date()));
}
}
编写配置类
/**
* quartz的配置类
*/
@Configuration
public class QuartzConfig {
@Bean
public JobDetail testQuartz1() {
return JobBuilder.newJob(TestTask1.class).withIdentity("testTask1").storeDurably().build();
}
@Bean
public Trigger testQuartzTrigger1() {
//5秒执行一次
SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5)
.repeatForever();
return TriggerBuilder.newTrigger().forJob(testQuartz1())
.withIdentity("testTask1")
.withSchedule(scheduleBuilder)
.build();
}
@Bean
public JobDetail testQuartz2() {
return JobBuilder.newJob(TestTask2.class).withIdentity("testTask2").storeDurably().build();
}
@Bean
public Trigger testQuartzTrigger2() {
//cron方式,每隔5秒执行一次
return TriggerBuilder.newTrigger().forJob(testQuartz2())
.withIdentity("testTask2")
.withSchedule(CronScheduleBuilder.cronSchedule("*/5 * * * * ?"))
.build();
}
}
启动任务观察
可以正常的看到任务正常启动,任务Task被执行:
image-20210116214659029
6.几个框架之间的对比
Timer和ScheduledThreadPoolExecutor
Timer对调度的支持是基于绝对时间的,因此任务对系统时间的改变是敏感的;而ScheduledThreadPoolExecutor支持相对时间。
Timer使用单线程方式来执行所有的TimerTask,如果某个TimerTask很耗时则会影响到其他TimerTask的执行;而ScheduledThreadPoolExecutor则可以构造一个固定大小的线程池来执行任务。
Timer不会捕获由TimerTask抛出的未检查异常,故当有异常抛出时,Timer会终止,导致未执行完的TimerTask不再执行,新的TimerTask也不能被调度;ScheduledThreadPoolExecutor对这个问题进行了妥善的处理,不会影响其他任务的执行。
————————————————
版权声明:本文为CSDN博主「njitzyd」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_37687594/article/details/119943278
更多推荐
所有评论(0)