Spring之——兩種任務排程Scheduled和Async
阿新 • • 發佈:2018-11-06
轉載請註明出處:http://blog.csdn.net/l1028386804/article/details/72494169
1、Spring排程的兩種方式
Spring提供了兩種後臺任務的方法,分別是:
排程任務,@Schedule
非同步任務,@Async
當然,使用這兩個是有條件的,需要在spring應用的上下文中宣告
<task:annotation-driven/>當然,如果我們是基於java配置的,需要在配置哪裡加多EnableScheduling和@EnableAsync 就像下面這樣
-
@EnableScheduling
-
@EnableAsync
-
public
class WebAppConfig {
-
....
-
}
除此之外,還是有第三方庫可以呼叫的,例如Quartz.
2、@Schedule
先看下@Schedule怎麼呼叫再說
-
public
final
static
long ONE_DAY =
24 *
60 *
60 *
1000;
-
public
final
static
long ONE_HOUR =
60 *
60 *
1000;
-
-
@Scheduled(fixedRate = ONE_DAY)
-
public void scheduledTask() {
-
System.out.println(
" 我是一個每隔一天就會執行一次的排程任務");
-
}
-
-
@Scheduled(fixedDelay = ONE_HOURS)
-
public void scheduleTask2() {
-
System.out.println(
" 我是一個執行完後,隔一小時就會執行的任務");
-
}
-
-
@Scheduled(initialDelay=
1000, fixedRate=
5000)
-
public void doSomething() {
-
// something that should execute periodically
-
}
-
-
@Scheduled(cron =
"0 0/1 * * * ? ")
-
public void ScheduledTask3() {
-
System.out.println(
" 我是一個每隔一分鐘就就會執行的任務");
-
}
需要注意的
- 關於最後一個,在指定時間執行的任務,裡面使用的是Cron表示式,同時我們看到了兩個不一樣的面孔fixedDelay& fixedRate,前者fixedDelay表示在指定間隔執行程式,例如這個程式在今晚九點執行程式,跑完這個方法後的一個小時,就會再執行一次,而後者fixedDelay者是指,這個函式每隔一段時間就會被呼叫(我們這裡設定的是一天),不管再次排程的時候,這個方法是在執行還是結束了。而前者就要求是函式執行結束後開始計時的,這就是兩者區別。
- 這個還有一個initialDelay的引數,是第一次呼叫前需要等待的時間,這裡表示被呼叫後的,推遲一秒再執行,這適合一些特殊的情況。
- 我們在serviceImpl類寫這些排程任務時候,也需要在這些我們定義的serviceInterface的藉口中寫多這個介面,要不然會爆 but not found in any interface(s) for bean JDK proxy.Either pull the method up to an interface or
3、@Async
有時候我們會呼叫一些特殊的任務,任務會比較耗時,重要的是,我們不管他返回的後果。這時候我們就需要用這類的非同步任務啦,呼叫後就讓他去跑,不堵塞主執行緒,我們繼續幹別的。程式碼像下面這樣:
-
public void AsyncTask(){
-
-
@Async
-
public void doSomeHeavyBackgroundTask(int sleepTime) {
-
try {
-
Thread.sleep(sleepTime);
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
-
-
@Async
-
public Future<String> doSomeHeavyBackgroundTask() {
-
try {
-
Thread.sleep(
3000);
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
return
null;
-
}
-
-
public void printLog() {
-
System.out.println(
" i print a log ,time=" + System.currentTimeMillis());
-
}
-
-
}
我們寫個簡單的測試類來測試下
-
@RunWith(SpringJUnit4ClassRunner.class)
-
@WebAppConfiguration
-
@ContextConfiguration(classes = AsycnTaskConfig.class)
//要宣告@EnableASync
-
public
class AsyncTaskTest {
-
@Autowired
-
AsyncTask asyncTask;
-
@Test
-
public void AsyncTaskTest() throws InterruptedException {
-
if (asyncTask !=
null) {
-
asyncTask.doSomeHeavyBackgroundTask(
4000);
-
asyncTask.printLog();
-
Thread.sleep(
5000);
-
}
-
}
-
}
這感覺比我們手動開多一個執行緒方便多了,不想非同步的話直接把@Async去掉就可以了,另外如果你想要返回個結果的,這需要加多個Future<>,關於這個Future,完全可以寫多幾篇文章介紹,順便把FutureTask介紹了。如果想修改Spring boot的預設執行緒池配置,可以實現AsyncConfigurer.
需要注意的:
相對於@scheduled,這個可以有引數和返回個結果,因為這個是我們呼叫的,而排程的任務是spring呼叫的。
非同步方法不能內部呼叫,只能像上面那樣,外部呼叫,否則就會變成阻塞主執行緒的同步任務啦!這個坑我居然跳下去了!例如下面這樣的。
-
public void AsyncTask(){
-
public void fakeAsyncTaskTest(){
-
doSomeHeavyBackgroundTask(
4000);
-
printLog();
-
//你會發現,當你像這樣內部呼叫的時候,居然是同步執行的,不是非同步的!!
-
}
-
-
@Async
-
public void doSomeHeavyBackgroundTask(int sleepTime) {
-
try {
-
Thread.sleep(sleepTime);
-
}
catch (InterruptedException e) {
-
e.printStackTrace();
-
}
-
}
-
-
public void printLog() {
-
System.out.println(
" i print a log ");
-
}
-
-
}
- 另外一點就是不要重複的掃描,這也會導致非同步無效,具體的可以看這個stackoveflow的spring-async-not-working Issue。
- 關於異常處理,難免在這個非同步執行過程中有異常發生,對於這個問題,spring提供的解決方案如下,實現 AsyncUncaughtExceptionHandler介面。
-
public
class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
-
@Override
-
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
-
// handle exception
-
}
-
}
寫好我們的異常處理後,我們需要配置一下,告訴spring,這個異常處理就是我們在執行非同步任務時候,丟擲錯誤時的異常終結者
-
@Configuration
-
@EnableAsync
-
public
class AsyncConfig implements AsyncConfigurer {
-
@Bean
-
public AsyncTask asyncBean() {
-
return
new AsyncTask();
-
}
-
-
@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();
-
}
-
}
4、Quartz登場
處理這兩個外,還有一個和spring整合的第三方庫叫Quartz
看了下官網的使用簡介,也是挺逗的,現在都習慣用maven,gradle之類來關係這些依賴了,他還叫人下載,也是不知為何,詳情點選->
http://quartz-scheduler.org/documentation/quartz-2.2.x/quick-start
估計有可能是因為沒再維護了的原因吧,看了下,最新版2.2居然是Sep, 2013更新的…
居然是停更的,不過Quartz作為一個企業級應用的任務排程框架,還是一個可以的候選專案的。
這裡不鋪開講,有興趣就去官網看下吧。整體用起來感覺是沒有spring自己的後臺任務方便,不過也可以接受,只需要簡單的配置就可以使用了。