使用@Async 注解实现简单异步调用

使用@Async 注解实现简单异步调用

一. 关于异步

1.1 异步是什么?

  • 与同步处理相对,异步处理不用阻塞当前线程来等待处理完成,而是允许后续操作,直至其它线程将处理完成,并回调通知此线程。
  • 通俗来讲,异步(async)是相对于同步(sync)而言的,很好理解。同步就是一件事一件事的执行。只有前一个任务执行完毕,才能执行后一个任务。而异步则是,当一件事情正在做的时候,可以同时做另外的事情,他们可以同时进行。

1.2 异步的优缺点?

  • 优点:
    1. 优雅,简洁,某些场景下易于理解,符合实际逻辑规范。
    2. 效率高,能够提升程序的执行速度,同时能够充分利用系统的多核优势
    3. 提升系统的稳定性,提升用户的体验等
  • 缺点:
    1. 可控性降低
    2. 复杂度提高
    3. 会更多的占用服务器资源

1.3 对同步异步的理解

  • 同步:
    • 同步的代码在很多情况下, CPU其实是在等待中度过的, 比如等待一个网络连接, 等待MySQL服务器的数据返回异步的代码.
  • 异步:
    • 异步就是把这些等待的时间给充分利用起来了, 把网络连接, 访问数据库这种耗时的工作时都在注册一个callback或者event之后切换出来,让CPU先去干别的活(例如响应别的请求), 当网络连接,数据库返回结果时再回来执行刚才的callback中的代码,能够提升程序执行效率。

1.3 Java中实现异步的方式

  • AsyncRestTemplate
  • @Async注解: 参考文档
  • WebClient(Spring 5.0引入):参考文档
  • MQ

1.4 关于@Async?

  • 从Spring3开始提供了@Async注解,该注解可以被标注在方法上,以便异步地调用该方法。调用者将在调用时立即返回,方法的实际执行将提交给Spring TaskExecutor的任务中,由指定的线程池中的线程执行。
  • 对于一些较为简单的异步任务,我们可以通过 Spring的 @Async来实现异步。它仅需要2个注解就可以实现异步功能。
  • 在项目应用中,@Async调用线程池,推荐使用自定义线程池的模式。自定义线程池常用方案:重新实现接口AsyncConfigurer。 文章以最简单方式展开描述,即非线程池下配置的 @Async 快速入门;

二. 实战入门

2.1 示例异步

  • 异步的示例代码如图所示:
    使用@Async 注解实现简单异步调用_第1张图片

2.2 实现步骤:

  • 实现的环境为: SpringBoot项目,同时引入了一些常用starter,使程序能够正常运行且有Aysnc的相关支持jar包;
  • 实现步骤如下:
    1. 在Application启动类上添加 @EnableAsync 注解,如图所示:
      使用@Async 注解实现简单异步调用_第2张图片

      使用这个注解能够让程序支持 @Async注解,开启此注解的异步功能

    2. 定义一个异步类,可以在类上或者方法上添加 @Async 注解,代码如下:
      @Component
      class Service{
               
      
          @Async
          fun sayHello() {
               
              System.err.println("hello,world")
              Thread.sleep(10000)
              System.err.println("睡眠结束")
          }
      }
      
    3. 然后就可以自行定义接口或者方法,去调用这个异步方法啦。当调用者有多个任务时,当进行到步骤2的任务时,会单独给与一个线程去运行,主程序不会再继续等待而是异步执行其他任务了。
    4. 总结如下:
      1. 在启动类上添加@EnableAsync注解,开启异步
      2. 在需要进行异步的类上或者方法上添加@Async注解,使这个类下的所有方法或者此注解下的方法变成异步执行。

2.3 @Async注意点

  1. 所使用的@Async注解方法的类对象应该是Spring容器管理的bean对象;
    • 使用@Async的方法,需要进行@Autowired引入进来或者new 创建一个对象。
  2. 不要在同一个类里面调用异步方法
    • 原因是如果调用方和被调用方是在同一个类中,是无法产生切面的,@Async没有被Spring容器管理。
  3. 杜绝AopContext.currentProxy()
    • AopContext.currentProxy()破坏了面向接口的编程,也包含了很多潜在的坑,应该禁用。要用异步就好好新建一个单独的异步类。
  4. 没有定义线程池
    1. 如果不自定义异步方法的线程池默认使用SimpleAsyncTaskExecutor。SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。并发大的时候会产生严重的性能问题。
    2. 基于上方demo,同学们可以搜索解决线程池使用Async的方案)
  5. 循环依赖问题
    • 在Spring 启动过程中Async注解为类创建代理类,这样Spring 三级缓存中就没有单例bean的早期引用对象而是Async代理对象,缓存里面找不到bean原始早期对象就引起报错。解决方法是为注入的其他类增加懒加载@Lazy注解,解决循环依赖报错问题。

你可能感兴趣的:(#,java,工作汇总,Async,Async注解,Async异步,Spring异步,Java异步)