亚洲精品久久久中文字幕-亚洲精品久久片久久-亚洲精品久久青草-亚洲精品久久婷婷爱久久婷婷-亚洲精品久久午夜香蕉

您的位置:首頁技術文章
文章詳情頁

springboot如何配置定時任務

瀏覽:21日期:2023-04-11 14:29:12

概述

在Java環境下創建定時任務有多種方式:

使用while循環配合 Thread.sleep(),雖然稍嫌粗陋但也勉強可用 使用 Timer和 TimerTask 使用 ScheduledExecutorService 定時任務框架,如Quartz

在SpringBoot下執行定時任務無非也就這幾種方式(主要還是后兩種)。只不過SpringBoot做了許多底層的工作,我們只需要做些簡單的配置就行了。

通過注解實現定時任務

在SpringBoot中僅通過注解就可以實現常用的定時任務。步驟就兩步:

在啟動類中添加 @EnableScheduling注解

@EnableScheduling@SpringBootApplicationpublic class MyApplication { public static void main(String[] args) {SpringApplication.run(MyApplication.class, args); } }

在目標方法中添加 @Scheduled注解,同時在 @Scheduled注解中添加觸發定時任務的元數據。

@Scheduled(fixedRate = 1000) public void job() {System.out.println(Thread.currentThread().getId() + ' ----- job1 ----- ' + System.currentTimeMillis()); }

注意: 目標方法需要沒有任何參數,并且返回類型為 void 。

這里的定時任務元數據是“fixRate=1000”,意思是固定間隔每1000毫秒即執行一次該任務。

再來看幾個 @Schedule注解的參數:

fixedRate:設置定時任務執行的時間間隔,該值為當前任務啟動時間與下次任務啟動時間之差; fixedDelay:設置定時任務執行的時間間隔,該值為當前任務結束時間與下次任務啟動時間之差; cron:通過cron表達式來設置定時任務啟動時間,在Cron Generator網站可以直接生成cron表達式。

這樣創建的定時任務存在一個問題:如存在多個定時任務,這些任務會同步執行,也就是說所有的定時任務都是在一個線程中執行。

再添幾個定時任務來執行下看看:

@Scheduled(fixedRate = 1000) public void job1() {System.out.println(Thread.currentThread().getId() + ' ----- job1 ----- ' + System.currentTimeMillis()); } @Scheduled(fixedRate = 1000) public void job2() {System.out.println(Thread.currentThread().getId() + ' ----- job2 ----- ' + System.currentTimeMillis()); } @Scheduled(fixedRate = 1000) public void job3() {System.out.println(Thread.currentThread().getId() + ' ----- job3 ----- ' + System.currentTimeMillis()); }

代碼中一共創建了三個定時任務,每個定時任務的執行間隔都是1000毫秒,在任務體中輸出了執行任務的線程ID和執行時間。

看下執行結果:

20 ----- job3 ----- 157312056826320 ----- job1 ----- 157312056826320 ----- job2 ----- 157312056826320 ----- job3 ----- 157312056926420 ----- job1 ----- 157312056926420 ----- job2 ----- 157312056926420 ----- job3 ----- 157312057026320 ----- job1 ----- 157312057026320 ----- job2 ----- 1573120570263

可以看到這三個定時任務的執行有如下的特點:

所有的定時任務每次都是在同一個線程上執行; 雖然未必是job1第一個開始執行,但是每批任務的執行次序是固定的——這是由fixRate參數決定的

這樣的定時任務已經能夠覆蓋絕大部分的使用場景了,但是它的缺點也很明顯:前面的任務執行時間過長必然會影響之后的任務的執行。為了解決這個問題,我們需要異步執行定時任務。接下來的部分我們將主要著眼于如何實現異步執行定時任務。

通過@Async注解實現異步定時任務

最常用的方式是使用 @Async注解來實現異步執行定時任務。啟用 @Async注解的步驟如下:

在啟動類中添加 @EnableAsync注解:

@EnableAsync@EnableScheduling@SpringBootApplicationpublic class MyApplication { public static void main(String[] args) {SpringApplication.run(MyApplication.class, args); } }

在定時任務方法上添加 @Async注解

@Async @Scheduled(fixedRate = 1000) public void job1() {System.out.println(Thread.currentThread().getId() + ' ----- job1 ----- ' + System.currentTimeMillis()); }

我們為前面的三個定時任務都加上 @Async注解再運行看看:

25 ----- job1 ----- 157312178141524 ----- job3 ----- 157312178141526 ----- job2 ----- 157312178141530 ----- job3 ----- 157312178229831 ----- job1 ----- 157312178229932 ----- job2 ----- 157312178229925 ----- job2 ----- 157312178330435 ----- job3 ----- 157312178330636 ----- job1 ----- 1573121783306

通過輸出信息可以看到每個定時任務都在不同的線程上執行,彼此的執行次序和執行時間也互不影響,說明配置為異步執行已經成功。

通過配置實現異步定時任務

現在我們有必要稍稍深入了解下springboot定時任務的執行機制了。

springboot的定時任務主要涉及到兩個接口: TaskScheduler和 TaskExecutor。在springboot的默認定時任務實現中,這兩個接口的實現類是 ThreadPoolTaskScheduler和 ThreadPoolTaskExecutor。

ThreadPoolTaskScheduler負責實現任務的定時執行機制,而 ThreadPoolTaskExecutor則負責實現任務的異步執行機制。二者中, ThreadPoolTaskScheduler執行棧更偏底層一些。

盡管在職責上有些區別,但是兩者在底層上都是依賴java的線程池機制實現的: ThreadPoolTaskScheduler依賴的底層線程池是 ScheduledExecutorService,springboot默認為其提供的coreSize是1,所以默認的定時任務都是在一個線程中執行; ThreadPoolTaskExecutor依賴的底層線程池是 ThreadPoolExecutor,springboot默認為其提供的corePoolSize是8。

說到這里應該清楚了:我們可以不添加 @Async注解,僅通過調整 ThreadPoolTaskScheduler依賴的線程池的coreSize也能實現多線程異步執行;同樣的,即使添加了 @Async注解,將 ThreadPoolTaskExecutor依賴的線程池的corePoolSize設置為1,那定時任務還是只能在一個線程上同步執行。看下springboot的相關配置項:

spring: task: scheduling: pool:size: 1 execution: pool:core-size: 2

其中spring.task.scheduling是 ThreadPoolTaskScheduler的線程池配置項,spring.task.execution是 ThreadPoolExecutor的線程池配置項。

再稍稍擴展下: @Async注解的value屬性就是用來指明使用的 TaskExecutor實例的。默認值是空字符串,表示使用的是springboot自啟動的 TaskExecutor實例。如有需要,也可以使用自定義的 TaskExecutor實例,如下:

/** * 配置線程池 * @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('my-task-executor-');// 線程池對拒絕任務(無線程可用)的處理策略,目前只支持AbortPolicy、CallerRunsPolicy;默認為后者taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//調度器shutdown被調用時等待當前被調度的任務完成taskExecutor.setWaitForTasksToCompleteOnShutdown(true);//等待時長taskExecutor.setAwaitTerminationSeconds(60);taskExecutor.initialize();return taskExecutor; }

此外,還有一種做法是通過提供自定義的 TaskScheduler Bean實例來實現異步執行。要提供提供自定義的 TaskScheduler 實例,可以直接通過 @Bean注解聲明創建,也可以在 SchedulingConfigurer接口中配置。這些在后面我們會提到。

調用SpringBoot接口實現定時任務

有時候會需要將定時任務的定時元數據寫在數據庫或其他配置中心以便統一維護。這種情況就不是通過注解能夠搞定的了,此時我們需要使用springboot定時任務一些組件來自行編程實現。常用的組件包括 TaskScheduler、 Triger接口和 SchedulingConfigurer接口。

注意:因為我們用到了springboot的定時任務組件,所以仍然需要在啟動類上添加 @EnableScheduling注解。

Trigger接口

Trigger接口主要用來設置定時元數據。要通過程序實現定時任務就不能不用到這個接口。這個接口有兩個實現類:

PeriodicTrigger用來配置固定時長的定時元數據 CronTrigger用來配置cron表達式定時元數據

使用TaskScheduler接口

TaskScheduler接口前面我們提過,這個接口需要配合 Trigger接口一起使用來實現定時任務,看個例子:

@Autowired private TaskScheduler taskScheduler; public void job() {int fixRate = 10;taskScheduler.schedule(() -> System.out.println(' job4 ----- ' + System.currentTimeMillis()),new PeriodicTrigger(fixRate, TimeUnit.SECONDS)); }

在上面的代碼里,我們使用 @Autowired注解獲取了springbootr容器里默認的 TaskScheduler實例,然后通過 PeriodicTrigger設置了定時元數據,定時任務的任務體則是一個 Runable接口的實現(在這里只是輸出一行信息)。

因為默認的 TaskScheduler實例的線程池coreSize是1,所以如有多個并發任務,這些任務的執行仍然是同步的。要調整為異步可以在配置文件中配置,也可以通過提供一個自定義的 TaskScheduler實例來設置:

@Bean('taskScheduler') public TaskScheduler taskExecutor() {ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();executor.setPoolSize(20);executor.setThreadNamePrefix('my-task-scheduler');executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//調度器shutdown被調用時等待當前被調度的任務完成executor.setWaitForTasksToCompleteOnShutdown(true);//等待時長executor.setAwaitTerminationSeconds(60);return executor; }

使用SchedulingConfigurer接口

SchedulingConfigurer接口的主要用處是注冊基于 Trigger接口自定義實現的定時任務。

在實現 SchedulingConfigurer接口后,通常還需要使用 @Configuration注解(當然啟動類上的 @EnableScheduling注解也不能少)來聲明它實現類。

這個接口唯一的一個方法就是configureTasks,字面意思是配置定時任務。這個方法最重要的參數是一個 ScheduledTaskRegistrar定時任務注冊類實例,該類有8個方法,允許我們以不同的方式注冊定時任務。

簡單做了個實現:

@Configurationpublic class MyTaskConfigurer implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {taskRegistrar.addCronTask(() -> System.out.println(Thread.currentThread().getId() + ' --- job5 ----- ' + System.currentTimeMillis()),'0/1 * * * * ?'); taskRegistrar.addFixedDelayTask(() -> System.out.println(Thread.currentThread().getId() + ' --- job6 ----- ' + System.currentTimeMillis()),1000); taskRegistrar.addFixedRateTask(() -> System.out.println(Thread.currentThread().getId() + ' --- job7 ----- ' + System.currentTimeMillis()),1000); }}

這里我們只使用了三種注冊任務的方法,分別嘗試注冊了fixDelay、fixRate以及cron觸發的定時任務。

springboot會自動啟動注冊的定時任務。看下執行結果:

22 --- job7 ----- 157361361634922 --- job6 ----- 157361361635022 --- job5 ----- 157361361700122 --- job7 ----- 157361361735222 --- job6 ----- 157361361735322 --- job5 ----- 157361361806522 --- job7 ----- 157361361835022 --- job6 ----- 157361361835522 --- job5 ----- 1573613619002

在執行結果中可以看到這里的任務也是在單一線程同步執行的。要設置為異步執行也簡單,因為 SchedulingConfigurer接口的另一個作用就是為定時任務提供自定義的 TaskScheduler實例。來看下:

@Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();scheduler.setThreadNamePrefix('my-task-scheduler');scheduler.setPoolSize(10);scheduler.initialize();taskRegistrar.setTaskScheduler(scheduler); }

在這里,我將之前注冊的定時任務去掉了,目的是想驗證下這里的配置是否對注解實現的定時任務有效。經檢驗是可行的。當然對在configureTasks方法中配置的定時任務肯定也是有效的。我就不一一貼結果了。

另外,需要注意:如 SchedulingConfigurer接口實例已經注入,將無法再獲取到springboot默認提供的 TaskScheduler接口實例。

通過Quartz實現定時任務

Quartz是一個非常強大的定時任務管理框架。短短的一篇文章未必能介紹清楚Quartz的全部用法。所以這里只是簡單地演示下如何在springboot中是如何使用Quartz的。更多的用法建議優先參考Quartz官方文檔。

在spring-boot-web 2.0及之后的版本,已經自動集成了quartz,如果不使用spring-boot-web或使用較早的版本的話我們還需要加一些依賴:

<!-- quartz --> <dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId> </dependency> <!-- spring集成quartz --> <dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId> </dependency> <!-- SchedulerFactoryBean依賴了tx包中的PlatformTransactionManager類,因為quartz的分布式功能是基于數據庫完成的 --> <dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId> </dependency>

添加完成這些依賴后,springboot服務在啟動時也會自啟動內部的quartz。事實上springboot已經為我們準備好了幾乎全部的quartz的配置。我們要做的只是把自定義的任務填進去。

首先我們需要創建一個Job實例,來實現Job的具體行為。

@Componentpublic class MyQuartzJob extends QuartzJobBean { @Override protected void executeInternal(JobExecutionContext context) {JobDataMap map = context.getMergedJobDataMap();// 從作業上下文中取出KeyString key = map.getString('key');System.out.println(Thread.currentThread().getId() + ' -- job8 ---------------------->>>>' + key); } }

QuartzJobBean是Spring提供的Quartz Job抽象類。在實現這個類的時候我們可以獲取注入到spring中的其他Bean。

配置Job

@Configurationpublic class QuartzConfig implements InitializingBean { @Autowired private SchedulerFactoryBean schedulerFactoryBean; @Override public void afterPropertiesSet() throws Exception {config(); } private void config() throws SchedulerException {Scheduler scheduler = schedulerFactoryBean.getScheduler(); JobDetail jobDetail = buildJobDetail();Trigger trigger = buildJobTrigger(jobDetail);scheduler.scheduleJob(jobDetail, trigger); } private JobDetail buildJobDetail() {// 用來存儲交互信息JobDataMap dataMap = new JobDataMap();dataMap.put('key', 'zhyea.com'); return JobBuilder.newJob(MyQuartzJob.class).withIdentity(UUID.randomUUID().toString(), 'chobit-job').usingJobData(dataMap).build(); } private Trigger buildJobTrigger(JobDetail jobDetail) {return TriggerBuilder.newTrigger().forJob(jobDetail).withIdentity(jobDetail.getKey().getName(), 'chobit-trigger').withSchedule(CronScheduleBuilder.cronSchedule('0/1 * * * * ?')).build(); }}

在創建 QuartzConfig類的時候實現了 InitializingBean接口,目的是在 QuartzConfig實例及依賴類都完成注入后可以立即執行配置組裝操作。

這里面有幾個關鍵接口需要說明下:

SchedulerFactoryBean,Quartz Scheduler工廠類,springboot自動化配置實現; Scheduer,負責Quartz Job調度,可從工廠類實例獲取; JobDetail,執行Quartz Job封裝; Trigger,完成Quartz Job啟動。

還可以在配置文件中添加Quartz的配置:

spring: quartz: startupDelay: 180000 #這里是毫秒值

這里配置了讓Quartz默認延遲啟動3分鐘。

看下執行結果:

30 -- job8 ---------------------->>>>zhyea.com31 -- job8 ---------------------->>>>zhyea.com32 -- job8 ---------------------->>>>zhyea.com33 -- job8 ---------------------->>>>zhyea.com34 -- job8 ---------------------->>>>zhyea.com...

好了,就這些內容了。前面用到的程序都上傳到了GITHUB,有需要可以參考下。

參考文檔

Spring Task Execution and SchedulingScheduling TasksSpringBoot Quartz SchedulerSpring Boot Quartz Scheduler Example: Building an Email Scheduling appQuartz Scheduler Tutorials

以上就是springboot如何配置定時任務的詳細內容,更多關于springboot配置定時任務的資料請關注好吧啦網其它相關文章!

標簽: Spring
相關文章:
主站蜘蛛池模板: 亚洲成人一级 | 成视频年人黄网站免费视频 | 国产japan色系videos护士 国产jav | 一级毛片黄片 | 亚洲 成人 欧美 自拍 | 欧美日韩网 | 中文永久免费看电视网站入口 | 亚洲色图欧美视频 | 亚洲国产日产韩国欧美综合 | 欧美高清视频www夜色资源 | 亚洲国产欧美国产第一区二区三区 | 精新精新国产自在现拍欣赏网 | 国内精自视频品线六区免费 | 小明看看免费 | 国产一级视频久久 | 国产精品色片 | 91久久免费视频 | 国产精品日韩一区二区三区 | 国产精品一区二区久久精品涩爱 | 精品国产区一区二区三区在线观看 | 婷婷久久综合九色综合98 | 成人免费在线 | 视频在线观看一区二区 | 亚洲香蕉综合在人在线时看 | 91青青草视频在线观看 | 综合婷婷 | 日韩欧美亚洲一区精选 | 国产在线精品一区二区高清不卡 | 真人一级毛片 | 日本一区精品 | 欧美日本亚洲国产一区二区 | 免费看的一级片 | 欧美一区二区三区在线可观看 | 外国一级黄色毛片 | 国产精选第一页 | 黄色大片欧美 | 91精品日本久久久久久牛牛 | 国产成人精品免费视频大全可播放的 | 日韩精品久久久久久久电影 | 蜜臀在线观看 | 丁香综合 |