Quartz和springTask区别
在Quartz中,默认所有的定时任务都是并发去执行,无需等到上次任务是否执行完毕。
springtask默认单线程去执行定时任务,需要等待上一次任务执行完毕才会去执行下一个任务(
要想让同一时间并发执行,要配线程池
)。
springtask组件
创建两个类,MyTask1、MyTask2。比如两个类都有push方法,让两个类的push方法每个五秒中去执行一次。
cron表达式可以在
https://cron.qqe2.com/
里参考。
/task/MyTask1.java
@Component
public class MyTask1 {
private final Logger logger = LoggerFactory.getLogger(MyTask1.class);
// @Scheduled注解是Spring Boot提供的用于定时任务控制的注解,表明这是一个需要定时执行的方法
@Scheduled(cron = "0/5 * * * * ?")// 每隔五秒钟去执行一次这个方法
public void push() {
// 在这个方法里面定时的去推送消息
logger.info("执行定时任务MyTask1"+Thread.currentThread().getName());//打印线程名称
}
}
/task/MyTask2.java
@Component
public class MyTask2 {
private final Logger logger = LoggerFactory.getLogger(MyTask2.class);
@Scheduled(cron = "0/5 * * * * ?")
public void push() {
logger.info("执行定时任务MyTask2"+Thread.currentThread().getName());//打印线程名称
}
}
另外,启动类需要加上注解
@EnableScheduling
,这个注解表示开启定时任务。
启动后可以发现,springtask组件实现的定时任务默认是一个线程同步执行的状态,即第一个任务执行完之后第二个任务才开始执行。
为了解决这个问题,可以使用线程池。
在两个push方法上面加上以在项目中配置好的线程beanId,比如
@Async("threadPoolTaskExecutor")
。以及启动类加注解
@EnableAsync
,表示开启线程池的异步执行。
执行定时任务MyTask1 test-thread1
执行定时任务MyTask2 test-thread2
执行定时任务MyTask1 test-thread3 // 五秒后...
执行定时任务MyTask2 test-thread4 // 五秒后...
...
如果springtask与线程池一起使用的话,可以让定时任务同一时间内异步执行。
如果开发环境和线上环境的定时时间不一样的话,可以在各环境配置文件里配置不同的值,在push方法里引入配置项就行。
Quartz组件
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
/quartz/QuartzTaks1.java
public class QuartzTaks1 extends QuartzJobBean{
private final Logger logger = LoggerFactory.getLogger(QuartzTaks1.class);
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// 执行定时任务
logger.info("定时任务QuartzTaks1" + Thread.currentThread().getName());
}
}
/quartz/QuartzTaks2.java
public class QuartzTaks2 extends QuartzJobBean{
private final Logger logger = LoggerFactory.getLogger(QuartzTaks2.class);
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
// 执行定时任务
logger.info("定时任务QuartzTaks2" + Thread.currentThread().getName());
}
}
/quartz/JobConfig.java
@Configuration
public class JobConfig {
@Bean
public JobDetail quartzTaks1JobDetail() {// 任务实例(JobDetail)
//表示去启动QuartzTaks1定时任务,并给他一个标识,比如类名。withIdentity:任务的名称(唯一实例)
return JobBuilder.newJob(QuartzTaks1.class).withIdentity(QuartzTaks1.class.getName()).build();
}
// 创建调度器(trigger是触发器,任务何时运行、运行几次,它说了算。)
@Bean
public Trigger quartzTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ? ");
return TriggerBuilder.newTrigger().forJob(QuartzTaks1.class.getName())
.withSchedule(cronScheduleBuilder)
.storeDurably() //表示开启任务持久化
.build();
}
@Bean
public JobDetail quartzTaks2JobDetail() {
//表示去启动QuartzTaks1定时任务,并给他一个标识
return JobBuilder.newJob(QuartzTaks2.class).withIdentity(QuartzTaks2.class.getName()).build();
}
// 创建调度器
@Bean
public Trigger quartzTrigger() {
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule("0/5 * * * * ? ");
return TriggerBuilder.newTrigger().forJob(QuartzTaks2.class.getName())
.withSchedule(cronScheduleBuilder)
.storeDurably() //表示开启任务持久化
.build();
}
}
运行之后,可以看到每个五秒输出信息:
定时任务QuartzTaks2:QuartzSchedule_Worker-1
定时任务QuartzTaks1:QuartzSchedule_Worker-2
定时任务QuartzTaks2:QuartzSchedule_Worker-3 // 五秒后
定时任务QuartzTaks1:QuartzSchedule_Worker-4 // 五秒后
...
可以看到两个定时任务并发执行,线程名称也都不一样。
需要注意的是,MyTask1类的定时任务方法上(push)加@Transactional的话,会导致事务失效,正确的做法应该是将把多表操作的代码放到service里面去,比如放在test方法里面,然后把@Transactional加到service的test方法上,在push方法上去调用service的test方法就行。只有这样,才能让事务成功执行。