SpringBoot使用异步(@Async)提升接口效率

5,731 阅读2分钟

这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战

先简单介绍下同步和异步的概念:

同步:按顺序执行

异步:同时执行

在Java程序中,大部分代码基本都是同步执行的,如果程序使用异步执行的话,可以大大提高执行的效率。一般情况下,我们会使用多线程、中间件、还有@Async来实现程序的异步执行。

@Async

spring 3.x之后,就内置了@Async,下面直接来看如何使用@Async

  • 创建异步配置类

@EnableAsync 开启异步支持 配置线程池相关属性 可以自定义多个线程池,通过@Async("线程池名称")指定线程池

import java.util.concurrent.ThreadPoolExecutor;

/**
 * @author CTW
 */
@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(5);
        // 设置最大线程数
        executor.setMaxPoolSize(10);
        // 设置队列容量
        executor.setQueueCapacity(20);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(60);
        // 设置默认线程名称
        executor.setThreadNamePrefix("Test-Async");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}
  • @Async注解的使用

两种情况:1.方法无返回,2.方法有返回

@Async 可以写在接口上,也可以写在实现类上

  1. 无返回
@Async
public void testAsync1() {
    long startTime = System.currentTimeMillis();
    try {
        //模拟耗时
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long endTime = System.currentTimeMillis();
    System.out.println(Thread.currentThread().getName() + ":void testAsync1(),耗时:" + (endTime - startTime));
}
  1. 有返回
@Override
@Async
public Future<String> testAsync4() {
    long startTime = System.currentTimeMillis();
    try {
        //模拟耗时
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long endTime = System.currentTimeMillis();
    return new AsyncResult<>("testAsync4:耗时:" + (endTime - startTime));
}
  • 创建接口进行测试(无返回/有返回方法的调用及写法)

调用无返回

@GetMapping("testAsync")
public String testAsync() {
    asyncService.testAsync1();
    return "testAsync--请求成功!";
}

可以看到调用无返回方法打印的耗时 在这里插入图片描述 调用有返回

@GetMapping("testAsync2")
public String testAsync2() throws ExecutionException, InterruptedException {
    Future<String> stringFuture = asyncService.testAsync4();
    return "testAsync--请求成功!" + stringFuture.get();
}

可以看到调用有返回方法返回的耗时 在这里插入图片描述

  • 异步执行多个方法

Service

@Override
@Async
public Future<String> testAsync4() {
    long startTime = System.currentTimeMillis();
    try {
        //模拟耗时
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long endTime = System.currentTimeMillis();
    return new AsyncResult<>("testAsync4:耗时:" + (endTime - startTime));
}

@Override
@Async
public Future<String> testAsync5() {
    long startTime = System.currentTimeMillis();
    try {
        //模拟耗时
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long endTime = System.currentTimeMillis();
    return new AsyncResult<>("testAsync5:耗时:" + (endTime - startTime));
}

@Override
@Async
public Future<String> testAsync6() {
    long startTime = System.currentTimeMillis();
    try {
        //模拟耗时
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    long endTime = System.currentTimeMillis();
    return new AsyncResult<>("testAsync6:耗时:" + (endTime - startTime));
}

Controller

@GetMapping("testAsync4")
public String testAsync4() throws ExecutionException, InterruptedException {
    long startTime = System.currentTimeMillis();

    Future<String> stringFuture = asyncService.testAsync4();
    Future<String> stringFuture1 = asyncService.testAsync5();
    Future<String> stringFuture2 = asyncService.testAsync6();

    System.out.println(stringFuture.get());
    System.out.println(stringFuture1.get());
    System.out.println(stringFuture2.get());

    long endTime = System.currentTimeMillis();
    System.out.println("请求成功!总耗时:" + (endTime - startTime));
    return "请求成功!总耗时:" + (endTime - startTime);
}

可以看到调用的方法是同步执行的 在这里插入图片描述

  • 如果在主线程操作返回值对象,主线程会等待,还是顺序执行
@GetMapping("testAsync4")
    public String testAsync4() throws ExecutionException, InterruptedException {
        long startTime = System.currentTimeMillis();

        Future<String> stringFuture = asyncService.testAsync4();
        System.out.println(stringFuture.get());

        Future<String> stringFuture1 = asyncService.testAsync5();
        Future<String> stringFuture2 = asyncService.testAsync6();

        System.out.println(stringFuture1.get());
        System.out.println(stringFuture2.get());

        long endTime = System.currentTimeMillis();
        System.out.println("请求成功!总耗时:" + (endTime - startTime));
        return "请求成功!总耗时:" + (endTime - startTime);
    }

先执行testAsync4()拿到返回值,再执行testAsync5()、testAsync6()

大家一定要注意先调用完方法以后再操作返回对象,以免造成方法是同步执行的。

在这里插入图片描述

本篇关于@Async的介绍及使用先到这里,主要是介绍Controller层如何异步调用Service层,后续介绍下Service如何异步调用Dao层,持续更新