在項目中,當訪問其餘人的接口較慢或者作耗時任務時,不想程序一直卡在耗時任務上,想程序可以並行執行,咱們能夠使用多線程來並行的處理任務,也能夠使用spring提供的異步處理方式@Async。spring
調用以後,不返回任何數據。json
調用以後,返回數據,經過Future來獲取返回數據多線程
使用@EnableAsync啓用異步註解異步
@Configuration @EnableAsync @Slf4j public class AsyncConfig{ }
在異步處理的方法dealNoReturnTask上添加註解@Asyncasync
@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測試類:ide
@SpringBootTest(classes = SpringbootApplication.class) @RunWith(SpringJUnit4Cla***unner.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是爲了確認異步是否執行完成線程
異步調用返回數據,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判斷任務是否執行結束code
@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}
咱們能夠實現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()會捕獲指定異常,原有任務還會繼續運行,直到結束。
在有返回值的異步調用中,異步處理拋出異常,會直接拋出異常,異步任務結束,原有處理結束執行。