同步程式設計:在同步程式設計中,任務一次執行一個,只有當一個任務完成時,下一個任務才會被解除阻塞。
非同步程式設計:在非同步程式設計中,可以同時執行多個任務。您可以在上一個任務完成之前轉到另一個任務。
在Spring Boot
#中,我們可以使用@Async
註解來實現異步行為。
1.定義一個非同步服務介面AsyncService.java
public interface AsyncService { void asyncMethod() throws InterruptedException; Future<String> futureMethod() throws InterruptedException; }
2.實作定義的介面AsyncServiceImpl.java
@Service @Slf4j public class AsyncServiceImpl implements AsyncService { @Async @Override public void asyncMethod() throws InterruptedException { Thread.sleep(3000); log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName()); } @Async @Override public Future<String> futureMethod() throws InterruptedException { Thread.sleep(5000); log.info("Thread: [{}], Calling other service..", Thread.currentThread().getName()); return new AsyncResult<>("task Done"); } }
AsyncServiceImpl
是一個 spring
管理的 bean
。
您的非同步方法必須是公共的,而且是被@Async
註解修飾。
回傳類型被限制為 void
或 Future
。
3.定義一個控制器AsyncController.java
@EnableAsync @RestController @Slf4j public class AsyncController { @Autowired AsyncService asyncService; @GetMapping("/async") public String asyncCallerMethod() throws InterruptedException { long start = System.currentTimeMillis(); log.info("call async method, thread name: [{}]", Thread.currentThread().getName()); asyncService.asyncMethod(); String response = "task completes in :" + (System.currentTimeMillis() - start) + "milliseconds"; return response; } @GetMapping("/asyncFuture") public String asyncFuture() throws InterruptedException, ExecutionException { long start = System.currentTimeMillis(); log.info("call async method, thread name: [{}]", Thread.currentThread().getName()); Future<String> future = asyncService.futureMethod(); // 阻塞获取结果 String taskResult = future.get(); String response = taskResult + "task completes in :" + (System.currentTimeMillis() - start) + "milliseconds"; return response; } }
關鍵點,需要新增啟用異步的註解@EnableAsync
,當然這個註解加在其他地方也ok得。
當外部呼叫該介面時,asyncMethod()
將由預設任務執行程式建立的另一個執行緒執行,主執行緒不需要等待完成非同步方法執行。
4.運行一下
現在我們執行一下看看,是不是非同步回傳的。
可以看到呼叫/async
接口,最終一步呼叫了方法。
呼叫/asyncFuture
,發現回傳5秒多,難道不是非同步的嗎?其實也是異步的,看日誌可以看出來,只不過我們回傳的是Future
,呼叫Futrue.get()
是阻塞的。
我們現在看看如果異常方法中報錯了會怎麼樣?修改非同步程式碼如下所示,會拋運行時異常:
再次執行非同步接口,如下所示,會使用預設的執行緒池和異常處理。
我們也可以自訂非同步方法的處理例外和非同步任務執行器,我們需要設定 AsyncUncaughtExceptionHandler
,如下程式碼所示:
@Configuration public class AsynConfiguration extends AsyncConfigurerSupport { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(3); executor.setMaxPoolSize(4); executor.setThreadNamePrefix("asyn-task-thread-"); executor.setWaitForTasksToCompleteOnShutdown(true); executor.initialize(); return executor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncUncaughtExceptionHandler() { @Override public void handleUncaughtException(Throwable ex, Method method, Object... params) { System.out.println("Exception: " + ex.getMessage()); System.out.println("Method Name: " + method.getName()); ex.printStackTrace(); } }; } }
再次運行,得到的結果如下:
必須透過使用 @EnableAsync
註解註解主應用程式類別或任何直接或間接非同步方法呼叫程式類別來啟用非同步支援。主要透過代理模式實現,預設模式是 Proxy
,另一種是 AspectJ
。代理模式只允許透過代理攔截呼叫。永遠不要從定義它的同一個類別呼叫非同步方法,它不會起作用。
當使用 @Async
對方法進行註解時,它會根據「proxyTargetClass
」屬性為該物件建立一個代理程式。當 spring
執行這個方法時,預設情況下它會搜尋關聯的執行緒池定義。上下文中唯一的 spring
框架 TaskExecutor bean
或名為「taskExecutor
」的 #Executor bean
。如果這兩者都不可解析,預設會使用spring框架SimpleAsyncTaskExecutor
來處理非同步方法的執行。
以上是SpringBoot怎麼優雅地實現非同步調用的詳細內容。更多資訊請關注PHP中文網其他相關文章!