浅谈SpringBoot实现异步调用的几种方式

  目录

  一、使用 CompletableFuture 实现异步任务

  CompletableFuture 是 Java 8 新增的一个异步编程工具,它可以方便地实现异步任务。使用 CompletableFuture 需要满足以下条件:

  我们创建一个服务类,里面包含了异步方法和普通方法。

  import org.springframework.stereotype.Service;

  import java.util.concurrent.CompletableFuture;

  import java.util.concurrent.TimeUnit;

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion: 异步服务类

  */

  @Service

  public class AsyncService {

  /**

  * 普通任务操作1

  */

  public String task1() throws InterruptedException {

  TimeUnit.SECONDS.sleep(3);

  return "任务执行完成1";

  }

  /**

  * 普通任务操作2

  */

  public String task2() throws InterruptedException {

  TimeUnit.SECONDS.sleep(2);

  return "任务执行完成2";

  }

  /**

  * 普通任务操作3

  */

  public String task3() throws InterruptedException {

  TimeUnit.SECONDS.sleep(3);

  return "任务执行完成3";

  }

  /**

  * 异步操作1

  */

  public CompletableFuture asyncTask1() {

  return CompletableFuture.supplyAsync(() -> {

  try {

  TimeUnit.SECONDS.sleep(3);

  } catch (InterruptedException e) {

  throw new RuntimeException(e);

  }

  return "异步任务执行完成1";

  });

  }

  /**

  * 异步操作2

  */

  public CompletableFuture asyncTask2() {

  return CompletableFuture.supplyAsync(() -> {

  try {

  TimeUnit.SECONDS.sleep(2);

  } catch (InterruptedException e) {

  throw new RuntimeException(e);

  }

  return "异步任务执行完成2";

  });

  }

  /**

  * 异步操作3

  */

  public CompletableFuture asyncTask3() {

  return CompletableFuture.supplyAsync(() -> {

  try {

  TimeUnit.SECONDS.sleep(3);

  } catch (InterruptedException e) {

  throw new RuntimeException(e);

  }

  return "异步任务执行完成3";

  });

  }

  }

  我们先测试普通方法的情况,看看最后耗时

  import cn.hutool.core.date.StopWatch;

  import com.example.quartzdemo.service.AsyncService;

  import org.junit.jupiter.api.Test;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.boot.test.context.SpringBootTest;

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion: 异步处理测试

  */

  @SpringBootTest

  public class AsyncTest {

  @Autowired

  private AsyncService asyncService;

  @Test

  void test1() throws InterruptedException {

  StopWatch stopWatch = new StopWatch();

  stopWatch.start();

  // 异步操作

  /* CompletableFuture completableFuture1 = asyncService.asyncTask1();

  CompletableFuture completableFuture2 = asyncService.asyncTask2();

  CompletableFuture completableFuture3 = asyncService.asyncTask3();

  System.out.println(completableFuture1.get());

  System.out.println(completableFuture2.get());

  System.out.println(completableFuture3.get());*/

  // 同步操作

  System.out.println(asyncService.task1());

  System.out.println(asyncService.task2());

  System.out.println(asyncService.task3());

  stopWatch.stop();

  System.out.println("耗时:" + stopWatch.getTotalTimeMillis());

  }

  }

  程序执行的结果:

  任务执行完成1

  任务执行完成2

  任务执行完成3

  耗时:8008

  我们可以发现,普通同步方法是按顺序一个个操作的,各个方法不会同时处理。下面我们把这些操作换成异步的方法测试。

  import cn.hutool.core.date.StopWatch;

  import com.example.quartzdemo.service.AsyncService;

  import org.junit.jupiter.api.Test;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.boot.test.context.SpringBootTest;

  import java.util.concurrent.CompletableFuture;

  import java.util.concurrent.ExecutionException;

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion: 异步处理测试

  */

  @SpringBootTest

  public class AsyncTest {

  @Autowired

  private AsyncService asyncService;

  @Test

  void test1() throws InterruptedException, ExecutionException {

  StopWatch stopWatch = new StopWatch();

  stopWatch.start();

  // 异步操作

  CompletableFuture<String> completableFuture1 = asyncService.asyncTask1();

  CompletableFuture<String> completableFuture2 = asyncService.asyncTask2();

  CompletableFuture<String> completableFuture3 = asyncService.asyncTask3();

  System.out.println(completableFuture1.get());

  System.out.println(completableFuture2.get());

  System.out.println(completableFuture3.get());

  // 同步操作

  /*System.out.println(asyncService.task1());

  System.out.println(asyncService.task2());

  System.out.println(asyncService.task3());*/

  stopWatch.stop();

  System.out.println("耗时:" + stopWatch.getTotalTimeMillis());

  }

  }

  程序执行结果:

  异步任务执行完成1

  异步任务执行完成2

  异步任务执行完成3

  耗时:3008

  发现几个方法是异步同时进行的,没有先后的顺序,大大提高了程序执行效率。

  二、基于注解 @Async实现异步任务

  @Async 注解是 Spring 提供的一种轻量级异步方法实现方式,它可以标记在方法上,用来告诉 Spring 这个方法是一个异步方法,Spring 会将这个方法的执行放在异步线程中进行。使用 @Async 注解需要满足以下条件:

  import org.springframework.boot.SpringApplication;

  import org.springframework.boot.autoconfigure.SpringBootApplication;

  import org.springframework.scheduling.annotation.EnableAsync;

  @SpringBootApplication

  // 主类上加上这个注解,开启异步功能

  @EnableAsync

  public class QuartzDemoApplication {

  public static void main(String[] args) {

  SpringApplication.run(QuartzDemoApplication.class, args);

  }

  }

  修改测试的服务层

  import org.springframework.scheduling.annotation.Async;

  import org.springframework.scheduling.annotation.AsyncResult;

  import org.springframework.stereotype.Service;

  import java.util.concurrent.Future;

  import java.util.concurrent.TimeUnit;

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion: 异步服务类

  */

  @Service

  public class AsyncService {

  /**

  * 同步任务操作1

  */

  public String task1() throws InterruptedException {

  TimeUnit.SECONDS.sleep(3);

  return "任务执行完成1";

  }

  /**

  * 同步任务操作2

  */

  public String task2() throws InterruptedException {

  TimeUnit.SECONDS.sleep(2);

  return "任务执行完成2";

  }

  /**

  * 同步任务操作3

  */

  public String task3() throws InterruptedException {

  TimeUnit.SECONDS.sleep(3);

  return "任务执行完成3";

  }

  /**

  * 异步操作1

  */

  @Async

  public Future asyncTask1() throws InterruptedException {

  long currentTimeMillis = System.currentTimeMillis();

  TimeUnit.SECONDS.sleep(3);

  long currentTimeMillis1 = System.currentTimeMillis();

  System.out.println("task1任务耗时:" + (currentTimeMillis1 - currentTimeMillis) + "ms");

  return new AsyncResult<>("task1完成");

  }

  /**

  * 异步操作2

  */

  @Async

  public Future asyncTask2() throws InterruptedException {

  long currentTimeMillis = System.currentTimeMillis();

  TimeUnit.SECONDS.sleep(2);

  long currentTimeMillis1 = System.currentTimeMillis();

  System.out.println("task1任务耗时:" + (currentTimeMillis1 - currentTimeMillis) + "ms");

  return new AsyncResult<>("task2完成");

  }

  /**

  * 异步操作3

  */

  @Async

  public Future asyncTask3() throws InterruptedException {

  long currentTimeMillis = System.currentTimeMillis();

  TimeUnit.SECONDS.sleep(3);

  long currentTimeMillis1 = System.currentTimeMillis();

  System.out.println("task1任务耗时:" + (currentTimeMillis1 - currentTimeMillis) + "ms");

  return new AsyncResult<>("task3完成");

  }

  }

  创建一个测试类

  import com.example.quartzdemo.service.AsyncService;

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.web.bind.annotation.RequestMapping;

  import org.springframework.web.bind.annotation.RestController;

  import java.util.concurrent.ExecutionException;

  import java.util.concurrent.Future;

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion:

  */

  @RestController

  public class AsyncController {

  @Autowired

  private AsyncService asyncService;

  /**

  * 测试异步

  */

  @RequestMapping("/async")

  public String testAsync() throws InterruptedException, ExecutionException {

  long currentTimeMillis = System.currentTimeMillis();

  Future task1 = asyncService.asyncTask1();

  Future task2 = asyncService.asyncTask2();

  Future task3 = asyncService.asyncTask3();

  while (true) {

  if (task1.isDone() && task2.isDone() && task3.isDone()) {

  // 三个任务都调用完成,退出循环等待

  break;

  }

  }

  System.out.println(task1.get());

  System.out.println(task2.get());

  System.out.println(task3.get());

  long currentTimeMillis1 = System.currentTimeMillis();

  return "task任务总耗时:" + (currentTimeMillis1 - currentTimeMillis) + "ms";

  }

  }

  执行测试方法

  task1任务耗时:2006ms

  task1任务耗时:3011ms

  task1任务耗时:3011ms

  task1完成

  task2完成

  task3完成

  三、使用 TaskExecutor 实现异步任务

  TaskExecutor 是 Spring 提供的一个接口,它定义了一个方法 execute(),用来执行异步任务。使用 TaskExecutor 需要满足以下条件:

  创建一个异步配置类

  import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;

  import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler;

  import org.springframework.context.annotation.Bean;

  import org.springframework.context.annotation.Configuration;

  import org.springframework.core.task.TaskExecutor;

  import org.springframework.scheduling.annotation.AsyncConfigurer;

  import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

  import java.util.concurrent.Executor;

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion: 异步处理配置类

  */

  @Configuration

  public class AsyncConfig implements AsyncConfigurer {

  @Bean(name = "asyncExecutor")

  public TaskExecutor asyncExecutor() {

  ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

  executor.setCorePoolSize(10);

  executor.setMaxPoolSize(20);

  executor.setQueueCapacity(100);

  executor.setThreadNamePrefix("async-");

  executor.initialize();

  return executor;

  }

  @Override

  public Executor getAsyncExecutor() {

  return asyncExecutor();

  }

  @Override

  public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

  return new SimpleAsyncUncaughtExceptionHandler();

  }

  }

  修改下服务类,我们使用自定义的异步配置

  import org.springframework.beans.factory.annotation.Autowired;

  import org.springframework.beans.factory.annotation.Qualifier;

  import org.springframework.core.task.TaskExecutor;

  import org.springframework.scheduling.annotation.Async;

  import org.springframework.stereotype.Service;

  import java.util.concurrent.TimeUnit;

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion: 异步服务类

  */

  @Service

  public class AsyncService {

  @Autowired

  @Qualifier("asyncExecutor")

  private TaskExecutor taskExecutor;

  /**

  * 同步任务操作1

  */

  public String task1() throws InterruptedException {

  TimeUnit.SECONDS.sleep(3);

  return "任务执行完成1";

  }

  /**

  * 同步任务操作2

  */

  public String task2() throws InterruptedException {

  TimeUnit.SECONDS.sleep(2);

  return "任务执行完成2";

  }

  /**

  * 同步任务操作3

  */

  public String task3() throws InterruptedException {

  TimeUnit.SECONDS.sleep(3);

  return "任务执行完成3";

  }

  /**

  * 异步操作1

  */

  @Async

  public void asyncTask1() {

  taskExecutor.execute(() -> {

  try {

  TimeUnit.SECONDS.sleep(3);

  } catch (InterruptedException e) {

  throw new RuntimeException(e);

  }

  });

  System.out.println("异步任务执行完成1");

  }

  /**

  * 异步操作2

  */

  @Async

  public void asyncTask2() throws InterruptedException {

  taskExecutor.execute(() -> {

  try {

  TimeUnit.SECONDS.sleep(2);

  } catch (InterruptedException e) {

  throw new RuntimeException(e);

  }

  });

  System.out.println("异步任务执行完成2");

  }

  /**

  * 异步操作3

  */

  @Async

  public void asyncTask3() throws InterruptedException {

  taskExecutor.execute(() -> {

  try {

  TimeUnit.SECONDS.sleep(3);

  } catch (InterruptedException e) {

  throw new RuntimeException(e);

  }

  });

  System.out.println("异步任务执行完成3");

  }

  }

  测试类进行测试

  /**

  * @author qinxun

  * @date 2023-06-07

  * @Descripion: 异步处理测试

  */

  @SpringBootTest

  public class AsyncTest {

  @Autowired

  @Qualifier("asyncExecutor")

  private TaskExecutor taskExecutor;

  @Test

  void test1() {

  StopWatch stopWatch = new StopWatch();

  stopWatch.start();

  // 异步操作

  /* asyncService.asyncTask1();

  asyncService.asyncTask2();

  asyncService.asyncTask3();*/

  taskExecutor.execute(() -> System.out.println("异步任务"));

  // 同步操作

  /*System.out.println(asyncService.task1());

  System.out.println(asyncService.task2());

  System.out.println(asyncService.task3());*/

  stopWatch.stop();

  System.out.println("耗时:" + stopWatch.getTotalTimeMillis());

  }

  }

  耗时:6

  异步任务执行完成3

  异步任务执行完成1

  异步任务执行完成2

  到此这篇关于浅谈SpringBoot实现异步调用的几种方式的文章就介绍到这了,更多相关SpringBoot 异步调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

  您可能感兴趣的文章: