说到异步你们确定首先会先想到同步。咱们先来看看什么是同步?
所谓同步,就是发出一个功能调用时,在没有获得结果以前,该调用就不返回或继续执行后续操做。
简单来讲,同步就是必须一件一件事作,等前一件作完了才能作下一件事。
异步:异步就相反,调用在发出以后,这个调用就直接返回了,不须要等结果。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...后端