1. 程式人生 > >Spring之——兩種任務排程Scheduled和Async

Spring之——兩種任務排程Scheduled和Async

轉載請註明出處:http://blog.csdn.net/l1028386804/article/details/72494169

1、Spring排程的兩種方式

Spring提供了兩種後臺任務的方法,分別是:
    排程任務,@Schedule
    非同步任務,@Async
當然,使用這兩個是有條件的,需要在spring應用的上下文中宣告
<task:annotation-driven/>當然,如果我們是基於java配置的,需要在配置哪裡加多EnableScheduling和@EnableAsync 就像下面這樣


  
  1. @EnableScheduling
  2. @EnableAsync
  3. public class WebAppConfig {
  4. ....
除此之外,還是有第三方庫可以呼叫的,例如Quartz.

2、@Schedule

先看下@Schedule怎麼呼叫再說


  
  1. public final static long ONE_DAY = 24 * 60 * 60 * 1000;
  2. public final static
    long ONE_HOUR = 60 * 60 * 1000;
  3. @Scheduled(fixedRate = ONE_DAY)
  4. public void scheduledTask() {
  5. System.out.println( " 我是一個每隔一天就會執行一次的排程任務");
  6. }
  7. @Scheduled(fixedDelay = ONE_HOURS)
  8. public void scheduleTask2() {
  9. System.out.println( " 我是一個執行完後,隔一小時就會執行的任務");
  10. }
  11. @Scheduled(initialDelay= 1000, fixedRate= 5000)
  12. public void doSomething() {
  13. // something that should execute periodically
  14. }
  15. @Scheduled(cron = "0 0/1 * * * ? ")
  16. public void ScheduledTask3() {
  17. System.out.println( " 我是一個每隔一分鐘就就會執行的任務");
  18. }
需要注意的

  1.     關於最後一個,在指定時間執行的任務,裡面使用的是Cron表示式,同時我們看到了兩個不一樣的面孔fixedDelay& fixedRate,前者fixedDelay表示在指定間隔執行程式,例如這個程式在今晚九點執行程式,跑完這個方法後的一個小時,就會再執行一次,而後者fixedDelay者是指,這個函式每隔一段時間就會被呼叫(我們這裡設定的是一天),不管再次排程的時候,這個方法是在執行還是結束了。而前者就要求是函式執行結束後開始計時的,這就是兩者區別。
  2.     這個還有一個initialDelay的引數,是第一次呼叫前需要等待的時間,這裡表示被呼叫後的,推遲一秒再執行,這適合一些特殊的情況。
  3.     我們在serviceImpl類寫這些排程任務時候,也需要在這些我們定義的serviceInterface的藉口中寫多這個介面,要不然會爆 but not found in any interface(s) for bean JDK proxy.Either pull the method up to an interface or

3、@Async

有時候我們會呼叫一些特殊的任務,任務會比較耗時,重要的是,我們不管他返回的後果。這時候我們就需要用這類的非同步任務啦,呼叫後就讓他去跑,不堵塞主執行緒,我們繼續幹別的。程式碼像下面這樣:


  
  1. public void AsyncTask(){
  2. @Async
  3. public void doSomeHeavyBackgroundTask(int sleepTime) {
  4. try {
  5. Thread.sleep(sleepTime);
  6. } catch (InterruptedException e) {
  7. e.printStackTrace();
  8. }
  9. }
  10. @Async
  11. public Future<String> doSomeHeavyBackgroundTask() {
  12. try {
  13. Thread.sleep( 3000);
  14. } catch (InterruptedException e) {
  15. e.printStackTrace();
  16. }
  17. return null;
  18. }
  19. public void printLog() {
  20. System.out.println( " i print a log ,time=" + System.currentTimeMillis());
  21. }
  22. }
我們寫個簡單的測試類來測試下

  
  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @WebAppConfiguration
  3. @ContextConfiguration(classes = AsycnTaskConfig.class) //要宣告@EnableASync
  4. public class AsyncTaskTest {
  5. @Autowired
  6. AsyncTask asyncTask;
  7. @Test
  8. public void AsyncTaskTest() throws InterruptedException {
  9. if (asyncTask != null) {
  10. asyncTask.doSomeHeavyBackgroundTask( 4000);
  11. asyncTask.printLog();
  12. Thread.sleep( 5000);
  13. }
  14. }
  15. }
這感覺比我們手動開多一個執行緒方便多了,不想非同步的話直接把@Async去掉就可以了,另外如果你想要返回個結果的,這需要加多個Future<>,關於這個Future,完全可以寫多幾篇文章介紹,順便把FutureTask介紹了。如果想修改Spring boot的預設執行緒池配置,可以實現AsyncConfigurer.
需要注意的:
    相對於@scheduled,這個可以有引數和返回個結果,因為這個是我們呼叫的,而排程的任務是spring呼叫的。
    非同步方法不能內部呼叫,只能像上面那樣,外部呼叫,否則就會變成阻塞主執行緒的同步任務啦!這個坑我居然跳下去了!例如下面這樣的。

  
  1. public void AsyncTask(){
  2. public void fakeAsyncTaskTest(){
  3. doSomeHeavyBackgroundTask( 4000);
  4. printLog();
  5. //你會發現,當你像這樣內部呼叫的時候,居然是同步執行的,不是非同步的!!
  6. }
  7. @Async
  8. public void doSomeHeavyBackgroundTask(int sleepTime) {
  9. try {
  10. Thread.sleep(sleepTime);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. }
  15. public void printLog() {
  16. System.out.println( " i print a log ");
  17. }
  18. }

  1. 另外一點就是不要重複的掃描,這也會導致非同步無效,具體的可以看這個stackoveflow的spring-async-not-working Issue。
  2. 關於異常處理,難免在這個非同步執行過程中有異常發生,對於這個問題,spring提供的解決方案如下,實現 AsyncUncaughtExceptionHandler介面。

  
  1. public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
  2. @Override
  3. public void handleUncaughtException(Throwable ex, Method method, Object... params) {
  4. // handle exception
  5. }
  6. }
寫好我們的異常處理後,我們需要配置一下,告訴spring,這個異常處理就是我們在執行非同步任務時候,丟擲錯誤時的異常終結者

  
  1. @Configuration
  2. @EnableAsync
  3. public class AsyncConfig implements AsyncConfigurer {
  4. @Bean
  5. public AsyncTask asyncBean() {
  6. return new AsyncTask();
  7. }
  8. @Override
  9. public Executor getAsyncExecutor() {
  10. ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
  11. executor.setCorePoolSize( 7);
  12. executor.setMaxPoolSize( 42);
  13. executor.setQueueCapacity( 11);
  14. executor.setThreadNamePrefix( "MyExecutor-");
  15. executor.initialize();
  16. return executor;
  17. }
  18. @Override
  19. public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
  20. return new MyAsyncUncaughtExceptionHandler();
  21. }
  22. }

4、Quartz登場

處理這兩個外,還有一個和spring整合的第三方庫叫Quartz
看了下官網的使用簡介,也是挺逗的,現在都習慣用maven,gradle之類來關係這些依賴了,他還叫人下載,也是不知為何,詳情點選->
http://quartz-scheduler.org/documentation/quartz-2.2.x/quick-start
估計有可能是因為沒再維護了的原因吧,看了下,最新版2.2居然是Sep, 2013更新的…
居然是停更的,不過Quartz作為一個企業級應用的任務排程框架,還是一個可以的候選專案的。
這裡不鋪開講,有興趣就去官網看下吧。整體用起來感覺是沒有spring自己的後臺任務方便,不過也可以接受,只需要簡單的配置就可以使用了。