Spring 4.x Task 和 Schedule 概述(代java配置)


轉載請注明https://zhangzhaoyu.github.io/2016/09/30/spring-task-and-schedule-deep-research/

摘要

在很多業務場景中,系統都需要用到任務調度系統。例如定期地清理Redis 緩存,周期性地檢索某一條件並更新系統的資源等。在現代的應用系統中,快速地響應用戶的請求,是用戶體驗最主要的因素之一。因此在Web 系統中異步地執行任務,也會在很多場景中經常涉及到。本文對任務調度和異步執行的Java 實現進行了總結,主要講述一下內容:

  • Java 對異步執行和任務調度的支持
  • Spring 4.X 的異步執行和任務調度實現

Java 對異步執行和任務調度的支持

異步執行和任務調度底層的語言支撐都是Java 的多線程技術。線程是系統進行獨立運行和調度的基本單位。擁有了多線程,系統就擁有了同時處理多項任務的能力。

Java 實現異步調用

在Java 中要實現多線程有實現Runnable 接口和擴展Thread 類兩種方式。只要將需要異步執行的任務放在run() 方法中,在主線程中啟動要執行任務的子線程就可以實現任務的異步執行。如果需要實現基於時間點觸發的任務調度,就需要在子線程中循環的檢查系統當前的時間跟觸發條件是否一致,然后觸發任務的執行。該內容屬於Java 多線程的基礎知識,此處略過不講。

Java Timer 和 TimeTask 實現任務調度

為了便於開發者快速地實現任務調度,Java JDK 對任務調度的功能進行了封裝,實現了Timer 和TimerTask 兩個工具類。

TimerTask 類

由上圖,我們可以看出TimeTask 抽象類在實現Runnable 接口的基礎上增加了任務cancel() 和任務scheduledExecuttionTime() 兩個方法。

Timer 類

上圖為調度類Timer 的實現。從Timer類的源碼,可以看到其采用TaskQueue 來實現對多個TimeTask 的管理。TimerThread 集成自Thread 類,其mainLoop() 用來對任務進行調度。而Timer 類提供了四種重載的schedule() 方法和重載了兩種sheduleAtFixedRate() 方法來實現幾種基本的任務調度類型。下面的代碼是采用Timer 實現的定時系統時間打印程序。

public class PrintTimeTask extends TimerTask { @Override public void run() { System.out.println(new Date().toString()); } public static void main(String[] args) { Timer timer = new Timer("hello"); timer.schedule(new PrintTimeTask(), 1000L, 2000L); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Spring 4.x 中的異步執行和任務調度

Spring 4.x 中的異步執行

spring 作為一站式框架,為開發者提供了異步執行和任務調度的抽象接口TaskExecutor 和TaskScheduler。Spring 對這些接口的實現類支持線程池(Thread Pool) 和代理。 
Spring 提供了對JDK 中Timer和開源的流行任務調度框架Quartz的支持。Spring 通過將關聯的Schedule 轉化為FactoryBean 來實現。通過Spring 調度框架,開發者可以快速地通過MethodInvokingFactoryBean 來實現將POJO 類的方法轉化為任務。

Spring TaskExecutor

TaskExecutor 接口擴展自java.util.concurrent.Executor 接口。TaskExecutor 被創建來為其他組件提供線程池調用的抽象。

ThreadPoolTaskExecutor 是TaskExecutor 的最主要實現類之一。該類的核心繼承關系如下圖所示。 
ThreadPooltaskexecutor 類

ThreadPoolTaskExecutor 接口擴展了重多的接口,讓其具備了更多的能力。要實現異步需要標注@Async 注解:

  • AsyncTaskExecutor 增加了返回結果為Future 的submit() 方法,該方法的參數為Callable 接口。相比Runnable 接口,多了將執行結果返回的功能。
  • AsyncListenableTaskExecutor 接口允許返回擁有回調功能的ListenableFuture 接口,這樣在結果執行完畢是,能夠直接回調處理。
public class ListenableTask { @Async public ListenableFuture<Integer> compute(int n) { int sum = 0; for (int i = 0; i < n; i++) { sum += i; } return new AsyncResult<>(sum); } static class CallBackImpl implements ListenableFutureCallback<Integer> { @Override public void onFailure(Throwable ex) { System.out.println(ex.getMessage()); } @Override public void onSuccess(Integer result) { System.out.println(result); } } public static void main(String[] args) { ListenableTask listenableTask = new ListenableTask(); ListenableFuture<Integer> listenableFuture = listenableTask.compute(10); listenableFuture.addCallback(new CallBackImpl()); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • ThreadFactory 定義了創建線程的工廠方法,可以擴展該方法實現對Thread 的改造。

基於Java Config

  • 基於注解 當采用基於Java Config 注解配置時,只需要在主配置添加@EnableAsync 注解,Spring 會自動的創建基於ThreadPoolTaskExecutor 實例注入到上下文中。
@Configuration @EnableAsync public class AppConfig { }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
  • 基於AsyncConfigurer接口自定義 開發者可以自定義Executor 的類型,並且注冊異常處理器。
@Configuration public class TaskConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setMaxPoolSize(100); executor.setCorePoolSize(10); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { System.out.println(ex.getMessage()); } }; } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

基於XML Config

  • 基於傳統XML的配置 基於XML 的形式,采用傳統的Java Bean的形式配置ThreadPoolTaskExecutor。然后采用自動注入(autowire, resource,name)的可以直接在Spring Component 中注入Executor。以編程的形式實現異步任務。
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent. ThreadPoolTaskExecutor"> <property name="corePoolSize" value="5" /> <property name="maxPoolSize" value="10" /> <property name="queueCapacity" value="25" /> </bean>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 基於task 命名空間的配置 Spring 為任務的執行提供了便利的task 命名空間。當采用基於XML 配置時Spring 會自動地為開發者創建Executor。同時可以在annotation-driven 標簽上注冊實現了AsyncUncaughtExceptionHandler 接口的異常處理器。
<!-- config exception handler --> <bean id="taskAsyncExceptionHandler" class="org.zzy.spring4.application.schedulie.TaskAsyncExceptionHandler"/> <task:annotation-driven exception-handler="taskAsyncExceptionHandler" scheduler="scheduler" executor="executor"/>
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

異步執行的異常處理

除了上文提到的兩種異常處理方式,Spring 還提供了基於SimpleApplicationEventMulticaster 類的異常處理方式。

@Bean public SimpleApplicationEventMulticaster eventMulticaster(TaskExecutor taskExecutor) { SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(); eventMulticaster.setTaskExecutor(taskExecutor); eventMulticaster.setErrorHandler(new ErrorHandler() { @Override public void handleError(Throwable t) { System.out.println(t.getMessage()); } }); return eventMulticaster; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Spring 4.x 中任務調度實現

Spring 的任務調度主要基於TaskScheduler 接口。ThreadPoolTaskScheduler 是Spring 任務調度的核心實現類。該類提供了大量的重載方法進行任務調度。Trigger 定義了任務被執行的觸發條件。Spring 提供了基於Corn表達式的CornTrigger實現。TaskScheduler 如下圖所示。 
ThreadPoolTaskExecutor 類

實現TaskScheduler 接口的ThreadPoolTaskExecutor 繼承關系。 
ThreadPoolTaskExecutor 類

基於Java Config

  • 基於注解的配置 當采用基於Java Config 注解配置時,只需要在主配置添加@EnableScheduling 注解,Spring 會自動的創建基於ThreadPoolTaskExecutor 實例注入到上下文中。
@Configuration @EnableScheduling public class AppConfig { }
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4
  • 基於SchedulingConfigurer接口自定義
@Configuration public class ScheduleConfig implements SchedulingConfigurer { @Override public void configureTasks(ScheduledTaskRegistrar taskRegistrar) { taskRegistrar.setTaskScheduler(new ThreadPoolTaskScheduler()); taskRegistrar.getScheduler().schedule(new Runnable() { @Override public void run() { System.out.println("hello"); } }, new CronTrigger("0 15 9-17 * * MON-FRI")); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

基於XML Config

<task:annotation-driven scheduler="myScheduler"/> <task:scheduler id="myScheduler" pool-size="10"/>
  • 1
  • 2
  • 1
  • 2

@Scheduled 注解的使用

當某個Bean 由Spring 管理生命周期時,就可以方便的使用@Shcheduled 注解將該Bean 的方法准換為基於任務調度的策略。

@Scheduled(initialDelay=1000, fixedRate=5000) public void doSomething() { // something that should execute periodically } @Scheduled(cron="*/5 * * * * MON-FRI") public void doSomething() { // something that should execute on weekdays only }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

task 命名空間中的task:scheduled-tasks

該元素能夠實現快速地將一個普通Bean 的方法轉換為Scheduled 任務的途徑。具體如下:

<task:scheduled-tasks scheduler="myScheduler"> <task:scheduled ref="beanA" method="methodA" fixed-delay="5000" initial-delay="1000"/> <task:scheduled ref="beanB" method="methodB" fixed-rate="5000"/> <task:scheduled ref="beanC" method="methodC" cron="*/5 * * * * MON-FRI"/> </task:scheduled-tasks> <task:scheduler id="myScheduler" pool-size="10"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

總結

本文着重介紹了JDK 為任務調度提供的基礎類Timer。並在此基礎上詳細介紹了Spring 4.x 的異步執行和任務調度的底層接口設計。並針對常用的模式進行了講解,並附帶了源代碼。第三方開源的Quartz 實現了更為強大的任務調度系統,Spring 也對集成Quartz 提供了轉換。之后會擇機再詳細的介紹Quartz 的應用和設計原理。同時,Servlet 3.x 為Web 的異步調用提供了AsyncContext,對基於Web 的異步調用提供了原生的支持,后續的文章也會對此有相應的介紹。

參考引用

  1. Spring Doc Task and Schedule
  2. Quartz
  3. Corn Wiki
  4. Servlet AsyncContext


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM