SpringBoot 定時任務的使用
在JAVA開發領域,目前可以通過以下幾種方式進行定時任務:
Timer:jdk中自帶的一個定時調度類,可以簡單的實現按某一頻度進行任務執行。提供的功能比較單一,無法實現復雜的調度任務。
ScheduledExecutorService:也是jdk自帶的一個基於線程池設計的定時任務類。其每個調度任務都會分配到線程池中的一個線程執行,所以其任務是並發執行的,互不影響。
Spring Task:Spring提供的一個任務調度工具,支持註解和配置文件形式,支持Cron表達式,使用簡單但功能強大。
Quartz:一款功能強大的任務調度器,可以實現較為復雜的調度功能,如每月一號執行、每天淩晨執行、每周五執行等等,還支持分布式調度,就是配置稍顯復雜。
基於JDK方式實現簡單定時
剛剛有介紹過,基於JDK方式一共有兩種:Timer和ScheduledExecutorService。接下來,就簡單講解下這兩種方式。
Timer
Timer是jdk提供的java.util.Timer類。
簡單示例:
@GetMapping("/timer")
public String doTimer() {
timer.schedule(new TimerTask() {
@Override
public void run() {
log.info("Timer定時任務啟動:" + new Date());
}
}, 1000,1000);//延遲1秒啟動,每1秒執行一次
return "timer";
啟動後,訪問即可看見控制臺周期性輸出信息了:
2018-08-18 21:30:35.171 INFO 13352 --- [ Timer-0] c.l.l.s.c.controller.TaskController : Timer定時任務啟動:Sat Aug 18 21:30:35 CST 2018
2018-08-18 21:30:37.173 INFO 13352 --- [ Timer-0] c.l.l.s.c.controller.TaskController : Timer定時任務啟動:Sat Aug 18 21:30:37 CST 2018
2018-08-18 21:30:38.173 INFO 13352 --- [ Timer-0] c.l.l.s.c.controller.TaskController : Timer定時任務啟動:Sat Aug 18 21:30:38 CST 2018
2018-08-18 21:30:39.174 INFO 13352 --- [ Timer-0] c.l.l.s.c.controller.TaskController : Timer定時任務啟動:Sat Aug 18 21:30:39 CST 2018
......
相關API簡單說明:
1、在特定時間執行任務,只執行一次
public void schedule(TimerTask task,Date time)
2、在特定時間之後執行任務,只執行一次
public void schedule(TimerTask task,long delay)
3、指定第一次執行的時間,然後按照間隔時間,重復執行
public void schedule(TimerTask task,Date firstTime,long period)
4、在特定延遲之後第一次執行,然後按照間隔時間,重復執行
public void schedule(TimerTask task,long delay,long period)
5、第一次執行之後,特定頻率執行,與3同
public void scheduleAtFixedRate(TimerTask task,Date firstTime,long period)
6、在delay毫秒之後第一次執行,後按照特定頻率執行
public void scheduleAtFixedRate(TimerTask task,long delay,long period)
參數:
delay: 延遲執行的毫秒數,即在delay毫秒之後第一次執行
period:重復執行的時間間隔
取消任務使用:timer.cancel()方法即可註銷任務。
此類相對用的較少了,簡單了解下。
ScheduledExecutorService
ScheduledExecutorService可以說是Timer的替代類,因為Timer不支持多線程,任務是串行的,而且也不捕獲異常,假設某個任務異常了,整個Timer就無法運行了。
簡單示例:
@GetMapping("/executor")
public String ScheduledExecutorService() {
//
ScheduledExecutorService service = Executors.newScheduledThreadPool(10);
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
log.info("ScheduledExecutorService定時任務執行:" + new Date());
}
}, 1, 1, TimeUnit.SECONDS);//首次延遲1秒,之後每1秒執行一次
log.info("ScheduledExecutorService定時任務啟動:" + new Date());
return "ScheduledExecutorService!";
}
啟動後,可看見控制臺按設定的頻率輸出:
2018-08-18 22:03:24.840 INFO 6752 --- [nio-8080-exec-1] c.l.l.s.c.controller.TaskController : ScheduledExecutorService定時任務啟動:Sat Aug 18 22:03:24 CST 2018
2018-08-18 22:03:25.841 INFO 6752 --- [pool-1-thread-1] c.l.l.s.c.controller.TaskController : ScheduledExecutorService定時任務執行:Sat Aug 18 22:03:25 CST 2018
2018-08-18 22:03:26.842 INFO 6752 --- [pool-1-thread-1] c.l.l.s.c.controller.TaskController : ScheduledExecutorService定時任務執行:Sat Aug 18 22:03:26 CST 2018
2018-08-18 22:03:27.841 INFO 6752 --- [pool-1-thread-2] c.l.l.s.c.controller.TaskController : ScheduledExecutorService定時任務執行:Sat Aug 18 22:03:27 CST 2018
2018-08-18 22:03:28.840 INFO 6752 --- [pool-1-thread-1] c.l.l.s.c.controller.TaskController : ScheduledExecutorService定時任務執行:Sat Aug 18 22:03:28 CST 2018
2018-08-18 22:03:29.840 INFO 6752 --- [pool-1-thread-3] c.l.l.s.c.controller.TaskController : ScheduledExecutorService定時任務執行:Sat Aug 18 22:03:29 CST 2018
可同時設置多個任務,只需再次設置scheduleAtFixedRate即可。
常用方法說明:
ScheduleAtFixedRate:
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
參數說明:
command:執行線程
initialDelay:初始化延時
period:兩次開始執行最小間隔時間
unit:計時單位
ScheduleWithFixedDelay:
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
參數說明:
command:執行線程
initialDelay:初始化延時
delay:前一次執行結束到下一次執行開始的間隔時間(間隔執行延遲時間)
unit:計時單位
其他的方法大家可自行谷歌下。
基於SpingTask實現定時任務
使用SpringTask在SpringBoot是很簡單的,使用@Scheduled註解即可輕松搞定。
0.啟動類,加入@EnableScheduling讓註解@Scheduled生效。
@SpringBootApplication@EnableScheduling
br/>@EnableScheduling
public class Chapter22Application {
public static void main(String[] args) {
SpringApplication.run(Chapter22Application.class, args);
log.info("Chapter22啟動!");
}
}
1.編寫一個調度類,系統啟動後自動掃描,自動執行。
@Component@Slf4j
br/>@Slf4j
/**
* 自動掃描,啟動時間點之後5秒執行一次
*/
@Scheduled(fixedRate=5000)
public void getCurrentDate() {
log.info("Scheduled定時任務執行:" + new Date());
}
}
2.啟動後,控制臺可就看見每5秒一次輸出了:
2018-08-18 22:23:09.735 INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask : Scheduled定時任務執行:Sat Aug 18 22:23:09 CST 2018
2018-08-18 22:23:14.734 INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask : Scheduled定時任務執行:Sat Aug 18 22:23:14 CST 2018
2018-08-18 22:23:19.735 INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask : Scheduled定時任務執行:Sat Aug 18 22:23:19 CST 2018
2018-08-18 22:23:24.735 INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask : Scheduled定時任務執行:Sat Aug 18 22:23:24 CST 2018
2018-08-18 22:23:29.735 INFO 13812 --- [pool-1-thread-1] c.l.l.s.c.controller.ScheduledTask : Scheduled定時任務執行:Sat Aug 18 22:23:29 CST 2018
......
使用都是簡單的,現在我們來看看註解@Scheduled的參數意思:
fixedRate:定義一個按一定頻率執行的定時任務
fixedDelay:定義一個按一定頻率執行的定時任務,與上面不同的是,改屬性可以配合initialDelay, 定義該任務延遲執行時間。
cron:通過表達式來配置任務執行時間
Cron表達式詳解
一個cron表達式有至少6個(也可能7個)有空格分隔的時間元素。
依次順序如下表所示:
字段 允許值 允許的特殊字符
秒 0~59 , – /
分 0~59 , – /
小時 0~23 , – /
日期 1-31 , – ? / L W C
月份 1~12或者JAN~DEC , – /
星期 1~7或者SUN~SAT , – ? / L C #
年(可選) 留空,1970~2099 , – * /
簡單舉例:
0/1 ?:每秒執行一次
0 0 2 1 ? : 表示在每月的1日的淩晨2點調整任務
0 0 10,14,16 ? :每天上午10點,下午2點,4點
0 0 12 ? : 每天中午12點觸發
0 15 10 ? MON-FRI : 周一至周五的上午10:15觸發
在線表達式編輯器
自定義線程池
從控制臺輸出可以看見,多任務使用的是同一個線程。可結合上章節的異步調用來實現不同任務使用不同的線程進行任務執行。
0.編寫配置類,同時啟用@Async註解:
@Configuration@EnableAsync
br/>@EnableAsync
/**
- 配置線程池
- @return
*/
@Bean(name = "scheduledPoolTaskExecutor")
public ThreadPoolTaskExecutor getAsyncThreadPoolTaskExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(20);
taskExecutor.setMaxPoolSize(200);
taskExecutor.setQueueCapacity(25);
taskExecutor.setKeepAliveSeconds(200);
taskExecutor.setThreadNamePrefix("oKong-Scheduled-");
// 線程池對拒絕任務(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy;默認為後者
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//調度器shutdown被調用時等待當前被調度的任務完成
taskExecutor.setWaitForTasksToCompleteOnShutdown(true);
//等待時長
taskExecutor.setAwaitTerminationSeconds(60);
taskExecutor.initialize();
return taskExecutor;
}
}
1.調度類上加入@Async。
@Component@Slf4j
br/>@Slf4j
/**
* 自動掃描,啟動時間點之後5秒執行一次
*/
@Async("scheduledPoolTaskExecutor")
@Scheduled(fixedRate=5000)
public void getCurrentDate() {
log.info("Scheduled定時任務執行:" + new Date());
}
}
再次啟動程序,可看見控制臺輸出,任務已經是不同線程下執行了:
2018-08-18 22:47:13.313 INFO 14212 --- [ong-Scheduled-1] c.l.l.s.c.controller.ScheduledTask : Scheduled定時任務執行:Sat Aug 18 22:47:13 CST 2018
2018-08-18 22:47:13.343 INFO 14212 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2018-08-18 22:47:13.348 INFO 14212 --- [ main] c.l.l.s.chapter22.Chapter22Application : Started Chapter22Application in 2.057 seconds (JVM running for 2.855)
2018-08-18 22:47:13.348 INFO 14212 --- [ main] c.l.l.s.chapter22.Chapter22Application : Chapter22啟動!
2018-08-18 22:47:18.308 INFO 14212 --- [ong-Scheduled-2] c.l.l.s.c.controller.ScheduledTask : Scheduled定時任務執行:Sat Aug 18 22:47:18 CST 2018
動態添加定時任務
使用註解的方式,無法實現動態的修改或者添加新的定時任務的,這個使用就需要使用編程的方式進行任務的更新操作了。可直接使用ThreadPoolTaskScheduler或者SchedulingConfigurer接口進行自定義定時任務創建。
ThreadPoolTaskScheduler
ThreadPoolTaskScheduler是SpringTask的核心實現類,該類提供了大量的重載方法進行任務調度。這裏簡單示例下,具體的大家自行搜索下,用的少不太了解呀。
0.創建一個ThreadPoolTaskScheduler類。
@Bean("taskExecutor")
public TaskScheduler taskExecutor() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.setPoolSize(20);
executor.setThreadNamePrefix("oKong-taskExecutor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//調度器shutdown被調用時等待當前被調度的任務完成
executor.setWaitForTasksToCompleteOnShutdown(true);
//等待時長
executor.setAwaitTerminationSeconds(60);
return executor;
}
1.編寫一個控制類,動態設置定時任務:
@Autowired
TaskScheduler taskScheduler;
@GetMapping("/poolTask")
public String threadPoolTaskScheduler() {
taskScheduler.schedule(new Runnable() {
@Override
public void run() {
log.info("ThreadPoolTaskScheduler定時任務:" + new Date());
}
}, new CronTrigger("0/3 * * * * ?"));//每3秒執行一次
return "ThreadPoolTaskScheduler!";
}
2.啟動後,訪問接口,即可看見控制臺每3秒輸出一次:
2018-08-18 23:20:39.002 INFO 9120 --- [Kong-Executor-1] c.l.l.s.c.controller.TaskController : ThreadPoolTaskScheduler定時任務:Sat Aug 18 23:20:39 CST 2018
2018-08-18 23:20:42.000 INFO 9120 --- [Kong-Executor-1] c.l.l.s.c.controller.TaskController : ThreadPoolTaskScheduler定時任務:Sat Aug 18 23:20:42 CST 2018
2018-08-18 23:20:45.002 INFO 9120 --- [Kong-Executor-2] c.l.l.s.c.controller.TaskController : ThreadPoolTaskScheduler定時任務:Sat Aug 18 23:20:45 CST 2018
2018-08-18 23:20:48.001 INFO 9120 --- [Kong-Executor-1] c.l.l.s.c.controller.TaskController : ThreadPoolTaskScheduler定時任務:Sat Aug 18 23:20:48 CST 2018
SchedulingConfigurer
此類十個接口,直接實現其configurerTasks方法即可。
0.編寫配置類:
@Configuration@Slf4j
br/>@Slf4j
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(taskExecutor());
taskRegistrar.getScheduler().schedule(new Runnable() {
@Override
public void run() {
log.info("SchedulingConfigurer定時任務:" + new Date());
}
}, new CronTrigger("0/3 * * * * ?"));//每3秒執行一次
}
@Bean("taskExecutor")
public TaskScheduler taskExecutor() {
ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
executor.setPoolSize(20);
executor.setThreadNamePrefix("oKong-Executor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
//調度器shutdown被調用時等待當前被調度的任務完成
executor.setWaitForTasksToCompleteOnShutdown(true);
//等待時長
executor.setAwaitTerminationSeconds(60);
return executor;
}
}
1.啟動後,控制臺也可以看見每3秒輸出一次:
2018-08-18 23:24:39.001 INFO 868 --- [Kong-Executor-1] c.l.l.s.chapter22.config.ScheduleConfig : SchedulingConfigurer定時任務:Sat Aug 18 23:24:39 CST 2018
2018-08-18 23:24:42.001 INFO 868 --- [Kong-Executor-1] c.l.l.s.chapter22.config.ScheduleConfig : SchedulingConfigurer定時任務:Sat Aug 18 23:24:42 CST 2018
2018-08-18 23:24:45.000 INFO 868 --- [Kong-Executor-2] c.l.l.s.chapter22.config.ScheduleConfig : SchedulingConfigurer定時任務:Sat Aug 18 23:24:45 CST 2018
基於Quartz實現定時調度
由於本章節是基於SpringBoot 1.x版本的,所以沒有基於Quartz的starter配置,這裏直接引入了Quartz相關依賴包來集成。
題外話:原本使用SpringMvc時,一般上都是通過xml文件,配置其org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean類進行具體執行任務的配置,指定執行的對象和方法。然後通過設置CronTriggerFactoryBean或者SimpleTriggerFactoryBean設置定時器,最後通過org.springframework.scheduling.quartz.SchedulerFactoryBean加入調度的trigger。所以,我們就使用javaConfig方式進行簡單集成下。
0.加入pom依賴
SpringBoot 定時任務的使用