Skip to content

异步请求与异步调用的区别 ​

看在前面 ​

一句话答案 ​

  • 两者的使用场景不同。异步请求用来解决并发请求对服务器造成的压力,从而提高对请求的吞吐量。异步调用是用来做一些非主线流程且不需要实时计算和响应的任务。比如同步日志到 kafka 中做日志分析。

  • 异步请求是会一直等待 response 的,最终需要把结果返回给客户端。而异步调用往往是立即返回给客户端响应、完成这次请求。至于异步调用的任务后台自己慢慢跑就行,客户端不会关心。

一、异步请求 ​

1.1 是什么 ​

  • 异步请求可以先释放容器分配给请求的线程与相关资源,减轻系统负担。响应被延后,在耗时处理(如长时间运算)完成后再返回给客户端。

  • 核心价值是增加了服务器对客户端请求的吞吐量。生产中并发量大时通常用 nginx 做集群负载或消息队列做请求缓冲,所以异步请求实际使用较少。

1.2 四种实现方式 ​

方式一:Servlet 原生

  • request.startAsync() 获取 AsyncContext,可添加 AsyncListener 监听开始、完成、异常、超时事件回调。

  • setTimeout(20000) 设置超时;asyncContext.start(...) 提交内部线程执行业务。

  • 业务完成时必须调用 asyncContext.complete() 通知请求结束——此时主线程早已释放。

方式二:返回 Callable

  • Controller 方法直接返回 Callable<String>,在 call() 中做耗时处理后返回结果。

  • 继承 WebMvcConfigurerAdapter 重写 configureAsyncSupport,可设置默认超时时间、自定义线程池(ThreadPoolTaskExecutor)、注册 TimeoutCallableProcessingInterceptor 处理超时。

方式三:WebAsyncTask

  • 在 Callable 外再包一层 WebAsyncTask<String>,构造时指定超时时间(如 3000 ms)。

  • wat.onTimeout(callable) 设置超时回调,返回自定义超时提示。

方式四:DeferredResult

  • 适合较复杂的业务,最主要的特点是可以在两个完全不相干的线程间通信。

  • 可设置超时时间、onTimeout 委托处理超时、onCompletion 完成后回调。

  • 把业务丢到自定义线程池执行,处理完后调用 result.setResult(...) 返回结果。

二、异步调用 ​

2.1 是什么、用在哪 ​

  • 异步调用比异步请求更常用。适用于与主线业务无关、无紧密性的任务(如记录日志),另起线程处理,让主线程继续执行其他业务。库内另有一篇《Spring异步调用@Async》可对照。

2.2 怎么用 ​

  • 启动类加 @EnableAsync 使 @Async 注解生效。

  • 在需异步执行的方法上加 @Async("threadPool"),threadPool 为自定义线程池。

2.3 默认线程池的坑 ​

  • @Async 默认使用 SimpleAsyncTaskExecutor,它不是真正的线程池。线程不重用,每次调用都新建线程(从控制台递增的线程名可以看出)。生产上建议自定义线程池。

2.4 @Async 失效的场景 ​

  • 同一个类内调用 @Async 方法(含内部类):Spring 创建的是代理对象。同类内部调用走的是自身而非代理,与 @Transactional、@Cache 失效同理。

  • 调用 static 方法;调用 private 方法。

2.5 同类调用失效的两种解法 ​

解法一:通过 ApplicationContext 拿自身代理

  • 注入 ApplicationContext,用 applicationContext.getBean(EmailController.class) 拿到代理对象再调用 @Async 方法(方法必须是 public 且非 static)。

解法二:暴露 CGLIB 代理,用 AopContext 手动获取

  • 启动类加 @EnableAspectJAutoProxy(exposeProxy = true)。

  • 调用处用 (EmailService) AopContext.currentProxy() 获取当前代理对象,再调用异步方法。

  • 辅助判断工具:AopUtils.isAopProxy() / isCglibProxy() / isJdkDynamicProxy()。

三、对比与总结 ​

维度异步请求异步调用
使用场景解决并发请求对服务器的压力,提高吞吐量非主线流程、不需实时响应的任务(如同步日志到 kafka)
是否等待响应一直等待 response,最终需返回结果给客户端立即返回响应完成请求,任务后台自己跑,客户端不关心
典型实现AsyncContext / Callable / WebAsyncTask / DeferredResult@EnableAsync + @Async + 自定义线程池
  • 两者使用场景和机制不同,不要混为一谈。@Async 失效问题的根源在于 Spring 的动态代理机制(AOP)——同类调用绕过了代理对象。

内容来源:原文 · 2026-10 整理回填。