一。Spring提供了兩種調(diào)度任務(wù)的方式。
調(diào)度任務(wù),@Scheduled
異步任務(wù),@Async
1)spring的定時(shí)任務(wù)默認(rèn)是單線程,多個(gè)任務(wù)執(zhí)行起來時(shí)間會(huì)有問題。
2)不論定時(shí)任務(wù)被安排在多少個(gè)class類中,其依然是單線程執(zhí)行定時(shí)任務(wù)(串行任務(wù))。
3)定時(shí)任務(wù)并行處理,需手動(dòng)配置。
4)有多個(gè)web容器實(shí)例,scheduler會(huì)在多個(gè)實(shí)例上同時(shí)運(yùn)行。
5)shedule流程初始化。
首先spring容器初始化-->通過反射bean初始化之后-->對(duì)實(shí)例化的bean進(jìn)行class上的注解掃描-->判斷class中是否有@Scheduled注解-->若有則判斷調(diào)度的模式--> ScheduledAnnotationBeanPostProcessor處理,將掃描到的@Scheduled方法封裝成Runnable,交給線程池(默認(rèn)單線程)執(zhí)行。
6)定時(shí)任務(wù)的方法中,一定不要出現(xiàn)“死循環(huán)”、“http持續(xù)等待無響應(yīng)”現(xiàn)象,否則會(huì)導(dǎo)致定時(shí)任務(wù)程序無法正常。
1,定時(shí)任務(wù)單線程執(zhí)行的時(shí)間問題。
1)bTask會(huì)因?yàn)閍Task的sleep(20)而延遲執(zhí)行。
@Component
public class TestTask {
@Scheduled(fixedRate = 1000 * 10) //每10秒執(zhí)行一次
public void aTask(){
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(DateTime.now().toDate())+"*********A任務(wù)每10秒執(zhí)行一次進(jìn)入測(cè)試");
}
}
@Component
public class TestTask2 {
@Scheduled(fixedRate = 5000) //每5秒執(zhí)行一次
public void bTask(){
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(DateTime.now().toDate())+"*********B任務(wù)每5秒執(zhí)行一次進(jìn)入測(cè)試");
}
}
2)實(shí)現(xiàn)SchedulingConfigurer和AsyncConfigurer配置線程池。
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
@Configuration//配置定時(shí)任務(wù)的線程池,支持定時(shí)任務(wù)并行處理
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(100);
}
}
@Configuration
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(7);
executor.setMaxPoolSize(42);
executor.setQueueCapacity(11);
executor.setThreadNamePrefix("MyExecutor-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new MyAsyncUncaughtExceptionHandler();
}
}
二,用法解釋
1,@Scheduled注解,添加到方法上,周期性的調(diào)用該方法
簡單的周期性的任務(wù)simple periodic scheduling
image.png
1)initialDelay: 第一次執(zhí)行方法需要等待時(shí)間,單位為毫秒ms。the number of milliseconds to wait before the first execution of the method.
(一般來說,不加該參數(shù),項(xiàng)目重啟,就會(huì)立刻執(zhí)行該方法。)
@Scheduled(initialDelay=1000, fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
2)fixedDelay: 固定的延遲后再次調(diào)用。當(dāng)方法第一次被調(diào)用結(jié)束后,延遲固定的ms后,再次調(diào)用。
from the completion time of each preceding invocation.
從上次調(diào)用的完成時(shí)間起。
@Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
3)fixedRate 方法每fixedRate毫秒,將被調(diào)用一次,不管上次是否結(jié)束。
would be executed every fixedRate ms
@Scheduled(fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
2,cron表達(dá)式。
1)cron表達(dá)式, 使用6 - 7 個(gè)域
Seconds(秒) Minutes(分) Hours(時(shí)) DayOfMonth(第幾日) Month(月) DayOfWeek(星期幾, 1=星期天,2=星期一) Year(年,可選)
*:表示匹配該域的任意值
,逗號(hào): 指定觸發(fā)
-:范圍觸發(fā)
/:步進(jìn)觸發(fā)。
2)eg:
"0 0-5 14 * * ?" 在每天下午2點(diǎn)到下午2:05范圍內(nèi),每1分鐘觸發(fā) 一次
"0 0/5 14,18 * * ?" 指定小時(shí)14,18點(diǎn),指定步進(jìn)值5分鐘,在每天下午2點(diǎn)到2:55期間和下午6點(diǎn)到6:55期間的每5分鐘觸發(fā)一次。
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should execute on weekdays only
}
三,配置定時(shí)任務(wù)的線程池。
1)配置scheduledThreadPool
public class AppConfig implements SchedulingConfigurer{
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
logger.info("Configure task registor: {}", taskRegistrar);
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(20);
}
}
2)定時(shí)任務(wù)中使用hibernate操作。no hibernate session bound to thread
需要在service層添加@Transactional注解。
@Scheduled 標(biāo)注的方法最后是包裝到 ScheduledMethodRunnable 中被執(zhí)行的,它是一個(gè) Runnable 接口的實(shí)現(xiàn)
沒有添加事務(wù)支持,就不能從線程資源中獲取Session