• @Async的用法和示例


    @Async 注解的用法和示例

    背景

    通常,在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注解

    /**
     * 异步方法
     * 默认情况下,Spring 使用 SimpleAsyncTaskExecutor 去执行这些异步方法(此执行器没有限制线程数)。
     * 此默认值可以从两个层级进行覆盖:
     * 方法级别
     * 应用级别
     */
    @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方法

    /**
     * 异步方法不能是 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的方法

    /**
     * 调用不同类的异步方法,异步方法是 static 方法
     */
    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\)
    相信坚持的力量!

  • 相关阅读:
    在sql server中怎样获得正在执行的Sql查询
    在windows中使用VMWare安装Mac OS 10.7
    Scrspy 命令
    Windows Service 小品
    线程同步(一)
    线程基础必知必会(二)
    线程基础必知必会(一)
    准备工作与简介
    Python 正则表达式急速入门
    SQL Server 每日一题--每月销售额
  • 原文地址:https://www.cnblogs.com/kaesar/p/15982240.html
Copyright © 2020-2023  润新知