SpringBoot定時任務schedule講解
簡介:講解什么是定時任務和常見定時任務區別
一.定時任務
1、常見定時任務 Java自帶的java.util.Timer類
timer:配置比較麻煩,時間延后問題,不推薦
timertask:不推薦
2、Quartz框架(復雜定時任務可以使用,spring 或springmv項目)
配置更簡單
xml或者注解
具體說明后續......
3、SpringBoot使用注解方式開啟定時任務(springboot項目推薦使用)
1)啟動類里面 @EnableScheduling開啟定時任務,自動掃描
2)定時任務業務類 加注解 @Component被容器掃描
3)定時執行的方法加上注解 @Scheduled(fixedRate=2000) 定期執行一次 2秒
4、SpringBoot常用定時任務表達式配置和在線生成器 crontab 工具 https://tool.lu/crontab/
1)cron 定時任務表達式 @Scheduled(cron="*/1 * * * * *") 表示每秒
cron表達式,有專門的語法,而且感覺有點繞人,不過簡單來說,大家記住一些常用的用法即可,特殊的語法可以單獨去查。
cron一共有7位,但是最后一位是年,可以留空,所以我們可以寫6位:
* 第一位,表示秒,取值0-59
* 第二位,表示分,取值0-59
* 第三位,表示小時,取值0-23
* 第四位,日期天/日,取值1-31
* 第五位,日期月份,取值1-12
* 第六位,星期,取值1-7,星期一,星期二...,注:不是第1周,第二周的意思
另外:1表示星期天,2表示星期一。
* 第7為,年份,可以留空,取值1970-2099
cron中,還有一些特殊的符號,含義如下:
(*)星號:可以理解為每的意思,每秒,每分,每天,每月,每年...
(?)問號:問號只能出現在日期和星期這兩個位置,表示這個位置的值不確定,每天3點執行,所以第六位星期的位置,我們是不需要關注的,就是不確定的值。同時:日期和星期是兩個相互排斥的元素,通過問號來表明不指定值。比如,1月10日,比如是星期1,如果在星期的位置是另指定星期二,就前后沖突矛盾了。
(-)減號:表達一個范圍,如在小時字段中使用“10-12”,則表示從10到12點,即10,11,12
(,)逗號:表達一個列表值,如在星期字段中使用“1,2,4”,則表示星期一,星期二,星期四
(/)斜杠:如:x/y,x是開始值,y是步長,比如在第一位(秒) 0/15就是,從0秒開始,每15秒,最后就是0,15,30,45,60 另:*/y,等同於0/y
下面列舉幾個例子:
0 0 3 * * ? 每天3點執行
0 5 3 * * ? 每天3點5分執行
0 5 3 ? * * 每天3點5分執行,與上面作用相同
0 5/10 3 * * ? 每天3點的 5分,15分,25分,35分,45分,55分這幾個時間點執行
0 10 3 ? * 1 每周星期天,3點10分 執行,注:1表示星期天
0 10 3 ? * 1#3 每個月的第三個星期,星期天 執行,#號只能出現在星期的位置
2)fixedRate: 定時多久執行一次(上一次開始執行時間點后xx秒再次執行;)
3)fixedDelay: 上一次執行結束時間點后xx秒再次執行
4)fixedDelayString: 字符串形式,可以通過配置文件指定
5.定時任務單線程/多線程問題處理
1)問題描述,sprinboot按照如上方式配置定時任務,如果項目中有不止一個定時任務,並且有部分定時任務(執行時間較長或監聽類定時任務),會導致后面其他的定時任務無法執行
2)問題處理:提供兩種方案
方案一:
出現此問題的原因,是springboot默認的定時任務為單線程執行,即項目中的多個定時任務在同一線程中串行執行,一旦其中的某個定時任務出現執行時間較長或監聽服務、死循環等情況,其他定時任務將一直處於等待,
無法執行
附springboot源碼如下:
if (this.taskScheduler == null) {
this.localExecutor = Executors.newSingleThreadScheduledExecutor();//此處導致單線程問題
this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
}
解決方案:創建配置文件 ScheduleConfig.java
package com.liansheng.authority_service.cronJob.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import java.util.concurrent.Executors;
/**
* 多線程執行定時任務
* @author fjt
* 2020年3月27日
*/
//@Configuration
//所有的定時任務都放在一個線程池中,定時任務啟動時使用不同都線程。
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
//重新自定義定時任務線程池
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(15));//考慮具體項目中定時任務的多少及執行效率
}
}
此種方案下,如果某個定時任務出現處理時間較長或卡死、死循環之類的問題,此定時任務會一直等待,后期不會再次執行,不會影響線程的后期使用及其他定時任務。
方案二:
通過springboot的異步任務處理
··第一步:配置一個線程池,可以不配置,用默認線程池
第二步:在你的定時任務方法或類 上增加 @Async (如果使用自己配置的線程池,應為@Async(“自己創建的線程池名稱”),如果沒有配置線程池,則@Async)
第三步:啟動項目,發現各定時任務全部啟動,互不影響
此種方案下,如果某個定時任務出現處理時間較長或卡死、死循環之類的問題,此定時任務會定時在指定時間利用新的線程再次啟動該定時任務,會消耗掉大量線程,如果此任務一直無法釋放的話,運行時間較長后會導致系統奔潰,
如果使用此種方案建議檢查定時任務代碼質量及執行效率。優點是一個定時任務在出現異常后,不會影響下次指定時間再次執行。
以上問題是在項目中遇到的問題及處理方案總結,各有優缺點,需要根據項目具體情況,選擇使用,也可以混合使用。
二、SpringBoot2.x異步任務實戰(核心知識)
簡介:講解什么是異步任務,和使用SpringBoot2.x開發異步任務實戰
1、什么是異步任務和使用場景:適用於處理log、發送郵件、短信……等
一個service方法內需要異步調用多個方法,這些方法可以異步執行的情況下使用,提高后台效率。
下單接口->查庫存 100
余額校驗 150
風控用戶100
....
2、啟動類里面使用@EnableAsync注解開啟功能,自動掃描
3、定義異步任務類並使用@Component標記組件被容器掃描,異步方法加上@Async
注意點:
1)要把異步任務封裝到類里面,不能直接寫到Controller
2)增加Future<String> 返回結果 AsyncResult<String>("task執行完成");
3)如果需要拿到結果 需要判斷全部的 task.isDone()
4、通過注入方式,注入到controller里面,如果測試前后區別則改為同步則把Async注釋掉