如何在springboot中使用定时任务

1. 开启支持——在启动类上加注解

由于Spring Schedule已经包含在spring-boot-starter基础模块中了,所以不需要增加额外的依赖。

@SpringBootApplication
@EnableScheduling     <------这里加注解开启
public class TestApplication {

	public static void main(String[] args) {
		SpringApplication.run(TestApplication.class, args);
	}
}
2. 建立任务类——执行周期性的工作
@Component     <------这里要加注解
public class MyWork {

	@Scheduled(fixedRate = 5000)
	public void aWork() {
		Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
		System.out.println("每隔五秒钟遇见一个美女:" + sdf.format(date));
	}
}
3. 定时任务的两种类型
  • 间隔执行
@Scheduled(fixedRate = 5000) // 表示每隔 5000 毫秒执行一次
public void reportCurrentTime() {
    System.out.println("每隔五秒钟执行一次: " + dateFormat.format(new Date()));
}
  • 定时执行
@Scheduled(cron = "0 30 11 ? * *") // 表示在指定时间执行
public void fixTimeExecution() {
	SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日HH:mm:ss");
    System.out.println("在指定时间 " + sdf.format(new Date()) + "执行");
}

cron表达式的解释如下

4. 自定义线程池
  • springboot默认使用单线程池调度任务。
protected void scheduleTasks() {
    if (this.taskScheduler == null) {
        this.localExecutor = Executors.newSingleThreadScheduledExecutor();
        this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    }
    //省略...
}

对于多个任务而言,需要增加线程池的线程数量,避免任务都放在一个单线程中串行地执行,导致任务执行时间过长,运行的时效性不能保障。
注意:当方法的执行时间超过任务调度周期时,调度器会在下个周期执行方法内容。

  • 自定义线程池
@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
	public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }
     
    @Bean(destroyMethod="shutdown")
    public Executor taskExecutor() {
        return Executors.newScheduledThreadPool(20);
    }
}

当然使定时任务并行执行还可以使用 @EnableAsync 和 @Async 这两个注解。

@Component
@EnableAsync
public class AsyncScheduledTasks {
    private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTasks.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    @Async
    @Scheduled(fixedDelay = 2000)
    public void reportCurrentTimeWithFixedDelay() {
        try {
            TimeUnit.SECONDS.sleep(3);
            log.info("Fixed Delay Task : The time is now {}", dateFormat.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
5. Web应用中的启动和关闭问题

我们知道通过spring加载或初始化的Bean,在服务停止的时候,spring会自动卸载(销毁)。但是由于线程是JVM级别的,如果用户在Web应用中启动了一个线程,那么这个线程的生命周期并不会和Web应用保持一致。也就是说,即使Web应用停止了,这个线程依然没有结束(死亡)。

解决方法:

  • 当前对象是通过spring初始化
    spring在卸载(销毁)实例时,会调用实例的destroy方法。通过实现DisposableBean接口覆盖destroy方法实现。在destroy方法中主动关闭线程。
@Component
public class MyTask implements DisposableBean{
    @Override
    public void destroy() throws Exception {
        //关闭线程或线程池
        ThreadPoolTaskScheduler scheduler = (ThreadPoolTaskScheduler)applicationContext.getBean("scheduler");
        scheduler.shutdown();
    }
    //省略...
}
  • 当前对象不是通过spring初始化(管理)
    那么我们可以增加一个Servlet上下文监听器,在Servlet服务停止的时候主动关闭线程。
public class MyTaskListenter implements ServletContextListener{
    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
        //关闭线程或线程池
    }
    //省略...
}
参考资料
  • https://www.cnblogs.com/skychenjiajun/p/9057379.html?utm_source=tuicool&utm_medium=referral
  • 如果希望动态设定并提交定时任务,可以参考这里
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值