springboot非同步呼叫@Async
阿新 • • 發佈:2018-12-19
1.使用背景
在專案中,當訪問其他人的介面較慢或者做耗時任務時,不想程式一直卡在耗時任務上,想程式能夠並行執行,我們可以使用多執行緒來並行的處理任務,也可以使用spring提供的非同步處理方式@Async。
2.非同步處理方式
- 呼叫之後,不返回任何資料。
- 呼叫之後,返回資料,通過Future來獲取返回資料
[email protected]不返回資料
使用@EnableAsync啟用非同步註解
@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig{
}
在非同步處理的方法dealNoReturnTask上添加註解@Async
@Component
@Slf4j
public class AsyncTask {
@Async
public void dealNoReturnTask(){
log.info("Thread {} deal No Return Task start", Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Thread {} deal No Return Task end at {}" , Thread.currentThread().getName(), System.currentTimeMillis());
}
}
Test測試類:
@SpringBootTest(classes = SpringbootApplication.class)
@RunWith(SpringJUnit4ClassRunner.class)
@Slf4j
public class AsyncTest {
@Autowired
private AsyncTask asyncTask;
@Test
public void testDealNoReturnTask (){
asyncTask.dealNoReturnTask();
try {
log.info("begin to deal other Task!");
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
日誌列印結果為:
begin to deal other Task!
AsyncExecutorThread-1 deal No Return Task start
AsyncExecutorThread-1 deal No Return Task end at 1499751227034
從日誌中我們可以看出,方法dealNoReturnTask()是非同步執行完成的。
dealNoReturnTask()設定sleep 3s是為了模擬耗時任務
testDealNoReturnTask()設定sleep 10s是為了確認非同步是否執行完成
[email protected]返回資料
非同步呼叫返回資料,Future表示在未來某個點獲取執行結果,返回資料型別可以自定義
@Async
public Future<String> dealHaveReturnTask() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("thread", Thread.currentThread().getName());
jsonObject.put("time", System.currentTimeMillis());
return new AsyncResult<String>(jsonObject.toJSONString());
}
測試類用isCancelled判斷非同步任務是否取消,isDone判斷任務是否執行結束
@Test
public void testDealHaveReturnTask() throws Exception {
Future<String> future = asyncTask.dealHaveReturnTask();
log.info("begin to deal other Task!");
while (true) {
if(future.isCancelled()){
log.info("deal async task is Cancelled");
break;
}
if (future.isDone() ) {
log.info("deal async task is Done");
log.info("return result is " + future.get());
break;
}
log.info("wait async task to end ...");
Thread.sleep(1000);
}
}
日誌列印如下,我們可以看出任務一直在等待非同步任務執行完畢,用future.get()來獲取非同步任務的返回結果
begin to deal other Task!
wait async task to end ...
wait async task to end ...
wait async task to end ...
wait async task to end ...
deal async task is Done
return result is {"thread":"AsyncExecutorThread-1","time":1499752617330}
4.異常處理
我們可以實現AsyncConfigurer介面,也可以繼承AsyncConfigurerSupport類來實現
在方法getAsyncExecutor()中建立執行緒池的時候,必須使用 executor.initialize(),
不然在呼叫時會報執行緒池未初始化的異常。
如果使用threadPoolTaskExecutor()來定義bean,則不需要初始化
@Configuration
@EnableAsync
@Slf4j
public class AsyncConfig implements AsyncConfigurer {
// @Bean
// public ThreadPoolTaskExecutor threadPoolTaskExecutor(){
// ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// executor.setCorePoolSize(10);
// executor.setMaxPoolSize(100);
// executor.setQueueCapacity(100);
// return executor;
// }
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("AsyncExecutorThread-");
executor.initialize(); //如果不初始化,導致找到不到執行器
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new AsyncExceptionHandler();
}
}
非同步異常處理類:
@Slf4j
public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
log.info("Async method: {} has uncaught exception,params:{}", method.getName(), JSON.toJSONString(params));
if (ex instanceof AsyncException) {
AsyncException asyncException = (AsyncException) ex;
log.info("asyncException:{}",asyncException.getErrorMessage());
}
log.info("Exception :");
ex.printStackTrace();
}
}
非同步處理異常類:
@Data
@AllArgsConstructor
public class AsyncException extends Exception {
private int code;
private String errorMessage;
}
- 在無返回值的非同步呼叫中,非同步處理丟擲異常,AsyncExceptionHandler的handleUncaughtException()會捕獲指定異常,原有任務還會繼續執行,直到結束。
- 在有返回值的非同步呼叫中,非同步處理丟擲異常,會直接丟擲異常,非同步任務結束,原有處理結束執行。
大家可以關注我的公眾號:不知風在何處,相互溝通,共同進步。