springboot 定时任务

  • Post author:
  • Post category:其他




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方法就行。只有这样,才能让事务成功执行。



版权声明:本文为PurineKing原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。