說到異步你們確定首先會先想到同步。咱們先來看看什麼是同步?
所謂同步,就是發出一個功能調用時,在沒有獲得結果以前,該調用就不返回或繼續執行後續操做。
簡單來講,同步就是必須一件一件事作,等前一件作完了才能作下一件事。
異步:異步就相反,調用在發出以後,這個調用就直接返回了,不須要等結果。html
瀏覽器發起一個request而後會一直待一個響應response,在這期間裏面它是阻塞的。好比早期咱們在咱們在逛電商平臺的時候買東西咱們打開一個商品的頁面,大體流程是否是多是這樣,每次打開一個頁面都是由一個線程從頭至尾來處理,這個請求須要進行數據庫的訪問須要把商品價格庫存啥的返回頁面,還須要去調用第三方接口,好比優惠券接口等咱們只有等到這些都處理完成後這個線程纔會把結果響應給瀏覽器,在這等結果期間這個線程只能一直在乾等着啥事情也不能幹。這樣的話是否是會有有必定的性能問題。大體的流程以下:前端
爲了解決上面同步阻塞的問題,再Servlet3.0發佈後,提供了一個新特性:異步處理請求。好比咱們仍是進入商品詳情頁面,這時候這個前端發起一個請求,而後會有一個線程來執行這個請求,這個請求須要去數據庫查詢庫存、調用第三方接口查詢優惠券等。這時候這個線程就不用幹等着呢。它的任務到這就完成了,又能夠執行下一個任務了。等查詢數據庫和第三方接口查詢優惠券有結果了,這時候會有一個新的線程來把處理結果返回給前端。這樣的話線程的工做量是不超級飽和,須要不停的幹活,連休息的機會都不給了。java
下面咱們就來看看Spring mvc 的幾種異步方式吧
https://docs.spring.io/spring...
在這個以前咱們仍是先簡單的回顧下Servlet 3.1的異步:web
/** 公衆號:java金融 * 使用Callable * @return */ @GetMapping("callable") public Callable<String> callable() { System.out.println(LocalDateTime.now().toString() + "--->主線程開始"); Callable<String> callable = () -> { String result = "return callable"; // 執行業務耗時 5s Thread.sleep(5000); System.out.println(LocalDateTime.now().toString() + "--->子任務線程("+Thread.currentThread().getName()+")"); return result; }; System.out.println(LocalDateTime.now().toString() + "--->主線程結束"); return callable; } public static String doBusiness() { // 執行業務耗時 10s try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } return UUID.randomUUID().toString(); }
上面就是Callable的一個執行流程,下面咱們來簡單的分析下源碼,看看是怎麼實現的:
咱們知道SpringMvc是能夠返回json格式數據、或者返回視圖頁面(html、jsp)等,SpringMvc是怎麼實現這個的呢?最主要的一個核心類就是org.springframework.web.method.support.HandlerMethodReturnValueHandler 咱們來看看這個類,這個類就是一個接口,總共就兩個方法;spring
boolean supportsReturnType(MethodParameter returnType); void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
上面這個咱們的請求是返回Callable<String> 這樣一個結果的,咱們會根據這個返回的類型去找全部實現了HandlerMethodReturnValueHandler 這個接口的實現類,最終咱們會根據返回類型經過supportsReturnType這個實現的方法找到一個對應的HandlerMethodReturnValueHandler 實現類,咱們根據返回類型是Callable而後就找到了實現類CallableMethodReturnValueHandler。
開啓異步線程的話也就是在handleReturnValue這個方法裏面了,感興趣的你們能夠動手去debug下仍是比較好調試的。數據庫
@GetMapping("completableFuture") public CompletableFuture<String> completableFuture() { // 線程池通常不會放在這裏,會使用static聲明,這只是演示 ExecutorService executor = Executors.newCachedThreadPool(); System.out.println(LocalDateTime.now().toString() + "--->主線程開始"); CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(IndexController::doBusiness, executor); System.out.println(LocalDateTime.now().toString() + "--->主線程結束"); return completableFuture; } @GetMapping("listenableFuture") public ListenableFuture<String> listenableFuture() { // 線程池通常不會放在這裏,會使用static聲明,這只是演示 ExecutorService executor = Executors.newCachedThreadPool(); System.out.println(LocalDateTime.now().toString() + "--->主線程開始"); ListenableFutureTask<String> listenableFuture = new ListenableFutureTask<>(()-> doBusiness()); executor.execute(listenableFuture); System.out.println(LocalDateTime.now().toString() + "--->主線程結束"); return listenableFuture; }
注:這種方式記得不要使用內置的不要使用內置的 ForkJoinPool線程池,須要本身建立線程池不然會有性能問題編程
@GetMapping("asynctask") public WebAsyncTask asyncTask() { SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor(); System.out.println(LocalDateTime.now().toString() + "--->主線程開始"); WebAsyncTask<String> task = new WebAsyncTask(1000L, executor, ()-> doBusiness()); task.onCompletion(()->{ System.out.println(LocalDateTime.now().toString() + "--->調用完成"); }); task.onTimeout(()->{ System.out.println("onTimeout"); return "onTimeout"; }); System.out.println(LocalDateTime.now().toString() + "--->主線程結束"); return task; }
@GetMapping("deferredResult") public DeferredResult<String> deferredResult() { System.out.println(LocalDateTime.now().toString() + "--->主線程("+Thread.currentThread().getName()+")開始"); DeferredResult<String> deferredResult = new DeferredResult<>(); CompletableFuture.supplyAsync(()-> doBusiness(), Executors.newFixedThreadPool(5)).whenCompleteAsync((result, throwable)->{ if (throwable!=null) { deferredResult.setErrorResult(throwable.getMessage()); }else { deferredResult.setResult(result); } }); // 異步請求超時時調用 deferredResult.onTimeout(()->{ System.out.println(LocalDateTime.now().toString() + "--->onTimeout"); }); // 異步請求完成後調用 deferredResult.onCompletion(()->{ System.out.println(LocalDateTime.now().toString() + "--->onCompletion"); }); System.out.println(LocalDateTime.now().toString() + "--->主線程("+Thread.currentThread().getName()+")結束"); return deferredResult; }
@GetMapping("call") public String call() { new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); return "這是個假異步"; }
這幾種異步方式都跟返回Callable 差很少,都有對應的HandlerMethodReturnValueHandler 實現類,無非就是豐富了本身一些特殊的api、好比超時設置啥的,以及線程池的建立是誰來建立,執行流程基本都是同樣的。json
站在巨人的肩膀上摘蘋果:
https://blog.csdn.net/f641385...後端