说到异步大家肯定首先会先想到同步。我们先来看看什么是同步?
所谓同步,就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。
简单来说,同步就是必须一件一件事做,等前一件做完了才能做下一件事。异步:异步就相反,调用在发出之后,这个调用就直接返回了,不需要等结果。
浏览器发起一个request然后会一直待一个响应response,在这期间里面它是阻塞的。比如早期我们在我们在逛电商平台的时候买东西我们打开一个商品的页面,大致流程是不是可能是这样,每次打开一个页面都是由一个线程从头到尾来处理,这个请求需要进行数据库的访问需要把商品价格库存啥的返回页面,还需要去调用第三方接口,比如优惠券接口等我们只有等到这些都处理完成后这个线程才会把结果响应给浏览器,在这等结果期间这个线程只能一直在干等着啥事情也不能干。这样的话是不是会有有一定的性能问题。大致的流程如下:
为了解决上面同步阻塞的问题,再Servlet3.0发布后,提供了一个新特性:异步处理请求。比如我们还是进入商品详情页面,这时候这个前端发起一个请求,然后会有一个线程来执行这个请求,这个请求需要去数据库查询库存、调用第三方接口查询优惠券等。这时候这个线程就不用干等着呢。它的任务到这就完成了,又可以执行下一个任务了。等查询数据库和第三方接口查询优惠券有结果了,这时候会有一个新的线程来把处理结果返回给前端。这样的话线程的工作量是不超级饱和,需要不停的干活,连休息的机会都不给了。
/** 公众号: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();
}
boolean supportsReturnType(MethodParameter returnType);
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
开启异步线程的话也就是在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、比如超时设置啥的,以及线程池的创建是谁来创建,执行流程基本都是一样的。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。