0
点赞
收藏
分享

微信扫一扫

如何在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表达式的解释如下

如何在springboot中使用定时任务_quartz

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) {
//关闭线程或线程池
}
//省略...
}

参考资料

  • 如果希望动态设定并提交定时任务,可以参考​​这里​​


举报

相关推荐

0 条评论