广告
返回顶部
首页 > 资讯 > 后端开发 > Python >JavaSpring注解之@Async的基本用法和示例
  • 619
分享到

JavaSpring注解之@Async的基本用法和示例

2024-04-02 19:04:59 619人浏览 安东尼

Python 官方文档:入门教程 => 点击学习

摘要

目录背景异步调用@Async介绍在spring中启用@Async示例一:基本使用方式示例二:在同一个类中调用异步方法示例三:异步方法是static方法示例四:在方法级别上修改默认的执

背景

通常,在Java中的方法调用都是同步调用,比如在A方法中调用了B方法,则在A调用B方法之后,必须等待B方法执行并返回后,A方法才可以继续往下执行。这样容易出现的一个问题就是如果B方法执行时间较长,则可能会导致调用A的请求响应迟缓,为了解决这种问题,可以使用Spirng的注解@Async来用异步调用的方式处理,当然也会有别的多线程方式解决此类问题,本文主要分析@Async在解决此类问题时的用法以及具体的示例。

异步调用

比如方法A调用方法B,如果B是一个异步方法,则A方法在调用B方法之后,不用等待B方法执行完成,而是直接往下继续执行别的代码。

@Async介绍

在Spring中,使用@Async标注某方法,可以使该方法变成异步方法,这些方法在被调用的时候,将会在独立的线程中进行执行,调用者不需等待该方法执行完成。

在Spring中启用@Async

使用@EnableAsync

@Slf4j
@SpringBootApplication
@ComponentScan(basePackages = {"com.kaesar.spring"})
@EnableAsync // 开启异步调用
public class Application {
    public static void main(String[] args) {
        log.info("Spring Boot开始启动...");
        ApplicationContext ctx = SpringApplication.run(Application.class, args);
        String[] activeProfiles = ctx.getEnvironment().getActiveProfiles();
        for (String profile : activeProfiles) {
            log.info("当前环境为:" + profile);
        }
        log.info("spring boot启动成功...");
    }
}

示例一:基本使用方式

在方法上添加@Async注解


@Async
public void test2() {
    try {
        log.info(Thread.currentThread().getName() + " in test2, before sleep.");
        Thread.sleep(2000);
        log.info(Thread.currentThread().getName() + " in test2, after sleep.");
    } catch (InterruptedException e) {
        log.error("sleep error.");
    }
}

调用异步方法


public void func1() {
    log.info("before call async function.");
    asyncService.test2();
    log.info("after call async function.");
    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        log.error("sleep error.");
    }
    log.info("func end.");
}

从执行结果可以看出,main线程中的func1方法在调用异步方法test2后,没有等待test2方法执行完成,直接执行后面的代码。

示例二:在同一个类中调用异步方法

方法func2和上面的异步方法test2方法在同一个类中

从执行结果可知,main线程中的func2方法在调用异步方法test2方法后,等待test2方法执行完后,才继续往后执行。

示例三:异步方法是static方法

异步方法test3是一个static方法


@Async
public static void test3() {
  try {
    log.info(Thread.currentThread().getName() + " in test3, before sleep.");
    Thread.sleep(2000);
    log.info(Thread.currentThread().getName() + " in test3, after sleep.");
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }

}

调用test3的方法


public void func3() {
  log.info(Thread.currentThread().getName() + ": before call async function.");
  AsyncService.test3();
  log.info(Thread.currentThread().getName() + ": after call async function.");
  try {
    Thread.sleep(3000);
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }
  log.info(Thread.currentThread().getName() + ": func end.");
}

执行结果。可以看出在static方法上添加@Async注解,当调用该方法时并没有新启用一个线程单独执行,而是按顺序执行代码,说明异步无效。

示例四:在方法级别上修改默认的执行器

自定义一个线程池执行器代替默认的执行器

自定义的线程池执行器

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;


@Configuration
public class AsyncConfig {
    private static final int MAX_POOL_SIZE = 10;
    private static final int CORE_POOL_SIZE = 5;

    @Bean("asyncTaskExecutor")
    public AsyncTaskExecutor asyncTaskExecutor() {
        ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
        asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
        asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
        asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
        asyncTaskExecutor.initialize();
        return asyncTaskExecutor;
    }
}

异步方法上使用自定义的执行器


@Async("asyncTaskExecutor")
public void test4() {
  try {
    log.info(Thread.currentThread().getName() + ": in test4, before sleep.");
    Thread.sleep(2000);
    log.info(Thread.currentThread().getName() + ": in test4, after sleep.");
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }
}

调用test4异步方法


public void func4() {
  log.info(Thread.currentThread().getName() + ": before call async function.");
  asyncService.test4();
  log.info(Thread.currentThread().getName() + ": after call async function.");
  try {
    Thread.sleep(3000);
  } catch (InterruptedException e) {
    log.error("sleep error.");
  }
  log.info(Thread.currentThread().getName() + ": func end.");
}

从执行结果可以看出,@Async注解声明使用指定的自定义的异步执行器,已经替换了默认的执行器。而且调用异步方法的main线程没有等待异步方法的执行。

说明:新建自定义的执行器后,注解@Async默认就会替换成自定义的执行器,所以在@Async注解上可以不用指定。

\(1.01^{365} ≈ 37.7834343329\)
\(0.99^{365} ≈ 0.02551796445\)
相信坚持的力量!

补充:Java中异步注解@Async的陷阱

或许,你在Java后端添加异步过程时会这样处理,然后摇摇大摆、灰溜溜地闪,而实际的运行结果却并不是我们期望的那样。那么,现在就将试验结果记录如下,以便少走弯路。

(一)在Controller层的公开接口直接添加@Async注解

前端调用该种接口时会立刻结束,意味着开始即结束,不会在乎该异步接口返回的数据,其实这种接口只适合前端下发命令,后续就不管后端的处理流程了,也不需要后端返回的对象。

(二)在Controller层的私有接口直接添加@Async注解

这种情况是,前端调用后端的公开接口并等待该接口返回,此时在该接口中调用了该层的添加了@Async注解的私有方法,也许你期待的是让后端接口立刻返回,让具体的处理过程放在@Async注解的私有函数中,可事实并没有达到你的效果,添加了@Async注解的私有函数依旧是同步过程,即使你在Controller层的类前面添加了@EnableAsync注解,也无济于事;所以,这种方式达不到异步的效果。我们可以通过日志来验证该过程,如下图所示:

在上图中,我们看到先进入Controller层公开接口,然后进入带有@Async注解的私有方法,接着跳出,最后又回到Controller层公开接口,整个流程就是同步过程,此时的@Async注解没有效果。

(三)在Service层的公开接口直接添加@Async注解

在Controller层提供同步流程的接口,只是在该层中会调用Service层的异步接口,只需要在需要用异步流程完成任务的接口上方添加@Async注解即可,这种策略是可以实现我们的异步过程的,我们还是通过日志来验证该流程,如下图所示:

在上图中,我们看到流程首先进入Controller层,然后立即跳出了Controller层,而Service层的异步接口就是后续完成的任务了,这样的流程已达到我们想要的异步过程了。

总结

到此这篇关于Java中@Async的基本用法和示例的文章就介绍到这了,更多相关java @Async的用法内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: JavaSpring注解之@Async的基本用法和示例

本文链接: https://www.lsjlt.com/news/141818.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作