浅谈SpringBoot实现异步调用的几种方式
作者:qinxun2008081
一、使用 CompletableFuture 实现异步任务
CompletableFuture 是 Java 8 新增的一个异步编程工具,它可以方便地实现异步任务。使用 CompletableFuture 需要满足以下条件:
异步任务的返回值类型必须是 CompletableFuture 类型;
在异步任务中使用 CompletableFuture.supplyAsync() 或 CompletableFuture.runAsync() 方法来创建异步任务;
在主线程中使用 CompletableFuture.get() 方法获取异步任务的返回结果。
我们创建一个服务类,里面包含了异步方法和普通方法。
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<String> asyncTask1() { return CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { throw new RuntimeException(e); } return "异步任务执行完成1"; }); } /** * 异步操作2 */ public CompletableFuture<String> asyncTask2() { return CompletableFuture.supplyAsync(() -> { try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { throw new RuntimeException(e); } return "异步任务执行完成2"; }); } /** * 异步操作3 */ public CompletableFuture<String> 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<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
耗时: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 注解需要满足以下条件:
需要在 Spring Boot 主类上添加 @EnableAsync 注解启用异步功能;
需要在异步方法上添加 @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<String> 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<String> 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<String> 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<String> task1 = asyncService.asyncTask1(); Future<String> task2 = asyncService.asyncTask2(); Future<String> 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 需要满足以下条件:
需要在 Spring 配置文件中配置一个 TaskExecutor 实例;
在异步方法中调用 TaskExecutor 实例的 execute() 方法来执行异步任务。
创建一个异步配置类
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 异步调用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!