一。Spring提供了兩種調度任務的方式。
調度任務,@Scheduled
異步任務,@Async
1)spring的定時任務默認是單線程,多個任務執行起來時間會有問題。
2)不論定時任務被安排在多少個class類中,其依然是單線程執行定時任務(串行任務)。
3)定時任務并行處理,需手動配置。
4)有多個web容器實例,scheduler會在多個實例上同時運行。
5)shedule流程初始化。
首先spring容器初始化-->通過反射bean初始化之后-->對實例化的bean進行class上的注解掃描-->判斷class中是否有@Scheduled注解-->若有則判斷調度的模式--> ScheduledAnnotationBeanPostProcessor處理,將掃描到的@Scheduled方法封裝成Runnable,交給線程池(默認單線程)執行。
6)定時任務的方法中,一定不要出現“死循環”、“http持續等待無響應”現象,否則會導致定時任務程序無法正常。
1,定時任務單線程執行的時間問題。
1)bTask會因為aTask的sleep(20)而延遲執行。
@Component
public class TestTask {
@Scheduled(fixedRate = 1000 * 10) //每10秒執行一次
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任務每10秒執行一次進入測試");
}
}
@Component
public class TestTask2 {
@Scheduled(fixedRate = 5000) //每5秒執行一次
public void bTask(){
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(sdf.format(DateTime.now().toDate())+"*********B任務每5秒執行一次進入測試");
}
}
2)實現SchedulingConfigurer和AsyncConfigurer配置線程池。
@Configuration
@EnableAsync
@EnableScheduling
public class AppConfig {
}
@Configuration//配置定時任務的線程池,支持定時任務并行處理
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注解,添加到方法上,周期性的調用該方法
簡單的周期性的任務simple periodic scheduling
image.png
1)initialDelay: 第一次執行方法需要等待時間,單位為毫秒ms。the number of milliseconds to wait before the first execution of the method.
(一般來說,不加該參數,項目重啟,就會立刻執行該方法。)
@Scheduled(initialDelay=1000, fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
2)fixedDelay: 固定的延遲后再次調用。當方法第一次被調用結束后,延遲固定的ms后,再次調用。
from the completion time of each preceding invocation.
從上次調用的完成時間起。
@Scheduled(fixedDelay=5000)
public void doSomething() {
// something that should execute periodically
}
3)fixedRate 方法每fixedRate毫秒,將被調用一次,不管上次是否結束。
would be executed every fixedRate ms
@Scheduled(fixedRate=5000)
public void doSomething() {
// something that should execute periodically
}
2,cron表達式。
1)cron表達式, 使用6 - 7 個域
Seconds(秒) Minutes(分) Hours(時) DayOfMonth(第幾日) Month(月) DayOfWeek(星期幾, 1=星期天,2=星期一) Year(年,可選)
*:表示匹配該域的任意值
,逗號: 指定觸發
-:范圍觸發
/:步進觸發。
2)eg:
"0 0-5 14 * * ?" 在每天下午2點到下午2:05范圍內,每1分鐘觸發 一次
"0 0/5 14,18 * * ?" 指定小時14,18點,指定步進值5分鐘,在每天下午2點到2:55期間和下午6點到6:55期間的每5分鐘觸發一次。
@Scheduled(cron="*/5 * * * * MON-FRI")
public void doSomething() {
// something that should execute on weekdays only
}
三,配置定時任務的線程池。
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)定時任務中使用hibernate操作。no hibernate session bound to thread
需要在service層添加@Transactional注解。
@Scheduled 標注的方法最后是包裝到 ScheduledMethodRunnable 中被執行的,它是一個 Runnable 接口的實現
沒有添加事務支持,就不能從線程資源中獲取Session