实现定时任务,Java 提供的 ScheduledExecutorService 是较好的解决方案,它可以自行设置并行任务数量和每个任务的定时执行方式,具体本文不详述。Spring 提供了更简便的定时任务实现方式:注解 @Scheduled,它的底层就是对 ScheduledExecutorService 实现的封装。
首先,在启动类或者某配置类上使用注解 @EnableScheduling 就开启了定时任务,否则下文的注解不生效。
其次,给某个方法加上 @Scheduled 注解即可将该方法设置为定时执行的方法,但要注意定时方法返回值只能是void且不能有传入参数。下面是 @Scheduled 支持的参数设置例子:
/**
* 每次方法执行完毕后,等待5s再执行此方法。
* 同时只能有个线程运行此方法
*/
@Scheduled(fixedDelay=5000)
/**
* 每隔5s调用一次此方法,无论之前的方法是否执行完毕
* 同时可能有N个线程执行此方法
*/
@Scheduled(fixedRate=5000)
/***
* initialDelay: 第一次调用此方法前的等待时间
*/
@Scheduled(initialDelay=1000, fixedRate=5000)
/**
* 支持cron语法:
* 每个参数的意义分别是: second, minute, hour, day of month, month, day of week
*
* 如下:周一至周五,每隔5s执行一次方法
*/
@Scheduled(cron="*/5 * * * * SUN-MON")
到这里已经搞定了使用定时任务的基本工作了,但如果是到此为止的话,项目中存在多个定时任务时,是都在同一个线程中执行的,同一时刻只能有一个任务在执行,这显然并不是我们希望的。
Spring 允许我们为定时任务设置多线程(或单线程)的线程池,如下 demo 代码所示:
@Configuration
@EnableScheduling // 启动定时任务
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
// 配置定时任务线程池,一般是有多少个@Scheduled声明的任务,corePoolSize就设置对应的数字
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(3));
}
}
其中 Executors.newScheduledThreadPool(3) 方法的传参 corePoolSize 表示核心线程数,从一开始就创建好的,即使没有任何任务需要执行,它们也会常驻后台。
通过这种方法创建的线程池并不意味着有多少个定时任务就必须设置多少个核心线程数,因为执行中的定时任务数量超过了核心线程数后,线程池会继续创建新的线程去执行更多任务,只是当线程空闲时最多保留 corePoolSize 个线程而已。如果有任务需要执行,已有的线程能够立刻进行工作,否则需要创建,仅此而已。
所以,当任务数量不是很多时,corePoolSize 设为相对的数量是OK的,当任务数量较多时就不再建议设置一个超大的 corePoolSize,应该根据实际情况考虑如何设置。
到这里我们才终于算是大功告成了。
—— write by NOT IN【 无知无识 无言无闻 】