先看一個簡單的示例:html
1 @RequestMapping("/getFuture") 2 public Future<String> getFuture() { 3 System.out.println(1); 4 // 必須是ListenableFuture的子類,或者CompletionStage子類,或者是DeferredResult 不能是FutureTask 5 // 如何返回CompletableFuture 6 CompletableFuture<String> ftu2 = CompletableFuture.supplyAsync(() -> { 7 try { 8 // RequestContextHolder.setRequestAttributes(attributes); 9 // 使用ThreadLocal相關特性時,須要在外面先get 10 Thread.sleep(3000L); 11 } catch (InterruptedException e) { 12 } finally { 13 // RequestContextHolder.resetRequestAttributes(); 14 } 15 return "THIS string"; 16 }, es); 17 18 //Future<String> future = executorService.submit(() -> { }); //返回值是FutureTask 19 20 return ftu2; 21 }
客戶端發起請求
===> DispatcherServlet根據返回值類型查找對應的handlar CallableMethodReturnValueHandler; DeferredResult,CallableFuture,ListenableFuture --> AsyncHandlerMethodReturnValueHandler
【Callable -->DeferredResultMethodReturnValueHandler】
===> Controller中返回Future對象給容器,
org.springframework.web.context.request.async.WebAsyncManager#startDeferredResultProcessing處理Future結果的監聽
===>
sleep3秒以後,Future完成後調用asyncWebRequest.dispatch() 從新發給Container,DispatcherServlet找到Message的序列化器將結果輸出。DispatcherServlet.doService會進入兩次!!!
異步線程的主要功能是:
業務處理耗較長(上面的sleep3)時,能夠先返回Future對象釋放Container的work線程, work線程能夠接收更多的請求。等Future完成以後從新調用Container的另外一個work線程,輸出response.
這裏work線程能夠是BIO中work線程, 也能夠是NIO中work線程
不是全部的Future都能在異步線程中處理
https://www.cnblogs.com/dennyzhangdd/p/7010972.html
spring boot中配置container的work線程數量
server.tomcat.max-threads=5
server.tomcat.min-spare-threads=3
使用異步處理的優勢:增長系統吞吐量,對響應速度提升不大,可能還會下降
缺點: 線程關係複雜, 異步超時、異常日誌攔截須要實現具體的Adapter接口,threadLocal很差清理
參考:《億級流量架構核心技術》http://jinnianshilongnian.iteye.com/blog/2245925
Servlet3.1 規範
tomcat線程配置 http://www.javashuo.com/article/p-zdpklgiu-bv.html
遺留問題: Future.get是何時調用的?
沒有直接調用get。 ListenerableFuture, CompletableFuture任務完成以後都會觸發回調
work1線程: 接收到請求---> DispatcherServlet--> return future -->WebAsyncManager#startDeferredResultProcessing
自定義的任務線程: CompletableFuture.postComplete以後調用 WebAsyncManager#setConcurrentResultAndDispatch
work2線程: dipatch ---> DispatcherServlet --> response.out
關於第二個任務線程:
當返回值是Callable時,調用WebAsyncManager#startCallableProcessing, 此時任務線程使用的是AsyncTaskExecutor,
能夠經過org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter#configureAsyncSupport設置自定義的AsyncTaskExecutor
當返回值是DeferredResult,ListenableFuture (Spring提供),CompletableFuture(JDK自帶)時,調用WebAsyncManager#startDeferredResultProcessing,此時任務線程池能夠直接指定
@See CompletableFuture.supply(xxxTask, threadPool)
@RequestMapping("/callableTest") public Callable<String> callableTest() { Callable<String> hello = () -> { // 此處不能設置線程池, WebAsyncManager中會使用AsyncSupportConfigurer的線程池 System.out.println("===>" + Thread.currentThread().getName()); return "HELLO"; }; return hello; }