目录
背景
实现
一、异步线程配置类
二、自定义异步异常统一处理类
三、实现调用异步(无回调-runAsync())
四、实现调用异步(有回调-supplyAsync())
五、异步执行错误异常示例
项目中总会有需要异步执行来避免浪费时间资源的情况,这就需要异步操作。异步又分两种:
1、无回调:有一些执行过程对用户而言不需要反馈回调,只需要自己执行即可,且执行过程时间较长(某些第三方接口,如发送短信验证码、查取ip属地等等),如果同步执行,势必会影响到用户体验,这时候就可以使用CompletableFuture.runAsync()方法了。
2、有回调:在执行异步操作结束后,需要获得异步方法返回的值,然后再回调给用户展示,这时候就需要用到CompletableFuture.supplyAsync()方法了。
/** * 异步线程配置类 */ @EnableAsync @Configuration public class AsyncConfig implements AsyncConfigurer { @Override public Executor getAsyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 设置核心线程数 executor.setCorePoolSize(8); // 设置最大线程数 executor.setMaxPoolSize(20); // 设置队列大小 executor.setQueueCapacity(Integer.MAX_VALUE); // 设置线程活跃时间(秒) executor.setKeepAliveSeconds(60); // 设置线程名前缀+分组名称 executor.setThreadNamePrefix("AsyncOperationThread-"); executor.setThreadGroupName("AsyncOperationGroup"); // 所有任务结束后关闭线程池 executor.setWaitForTasksToCompleteOnShutdown(true); // 初始化 executor.initialize(); return executor; } /** * 自定义异步异常 * @return */ @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new AsyncException(); } }
/** * 异步请求异常错误 */ public class AsyncException implements AsyncUncaughtExceptionHandler { @Override public void handleUncaughtException(Throwable throwable, Method method, Object... obj) { System.out.println("--------------------异步请求异常捕获---------------------------------"); System.out.println("Exception message - " + throwable.getMessage()); System.out.println("Method name - " + method.getName()); for (Object param : obj) { System.out.println("Parameter value - " + param); } System.out.println("---------------------异步请求异常捕获---------------------------------"); } }
在需要异步的方法前加上@Async再调用即可如下,在service层中新建一个方法,模拟第三方接口请求延时3秒(异步方法和调用方法一定要写在不同的类中 ,如果写在一个类中,是没有效果的!)
@Service public class LoginLogServiceImpl extends ServiceImplimplements ILoginLogService { // 无回调 @Async public CompletableFuture test() { return CompletableFuture.runAsync(() -> { // 模拟第三方请求加载时间 Thread.sleep(3000); System.out.println("保存成功登录日志"); }); } }
然后在controller层中调用该方法,
@PostMapping("/login") public Result login() throws InterruptedException { System.out.println("登录验证成功"); // 异步操作 iLoginLogService.test(); System.out.println("登录接口请求返回用户成功"); // 正常返回结果 return Result.success(200, "登录成功"); }
执行完成后,我们打开控制台,可以看到, 异步请求在接口执行正常,在接口返回结果后执行完成。
这里我们多采用3个异步线程来模拟实际效果,且延迟时间为1 6 3,看下他们的异步执行输出顺序是否和我们模拟的执行时间相同,如下:
@Service public class LoginLogServiceImpl extends ServiceImplimplements ILoginLogService { // 有返回值的异步方法CompletableFuture.supplyAsync() public CompletableFuture test1() { return CompletableFuture.supplyAsync(() -> { ThreadUtil.sleep(1000); System.out.println("test1异步请求结果有返回"); return "aaa"; }); } public CompletableFuture test2() { return CompletableFuture.supplyAsync(() -> { ThreadUtil.sleep(6000); System.out.println("test2异步请求结果有返回"); return "bbb"; }); } public CompletableFuture test3() { return CompletableFuture.supplyAsync(() -> { ThreadUtil.sleep(3000); System.out.println("test3异步请求结果有返回"); return "ccc"; }); } }
在controller层中利用CompletableFuture的get()方法获取数据,但是要加上try/catch捕获异常
@PostMapping("/login") public Result login() { System.out.println("登录验证成功"); // 异步保存登录成功日志 CompletableFuturefuture1 = iLoginLogService.test1(); CompletableFuture future2 = iLoginLogService.test2(); CompletableFuture future3 = iLoginLogService.test3(); System.out.println("模拟正常执行的方法"); String result1 = "111"; String result2 = "222"; String result3 = "333"; String result = ""; // 需要用try/catch来获取异步返回值 try { // get()方法获取返回值,并设置10秒超时就放弃,直接报错 result1 = future1.get(10, TimeUnit.SECONDS); result2 = future2.get(10, TimeUnit.SECONDS); result3 = future3.get(10, TimeUnit.SECONDS); System.out.println("返回结果为:" + result1); System.out.println("返回结果为:" + result2); System.out.println("返回结果为:" + result3); result = result1 + result2 + result3; // 正常返回结果 return Result.success(200, "登录成功", result); } catch (Exception e) { System.out.println(e); return Result.success(501, "登录失败,异常错误"); } }
结果可以看到如下图所示,异步线程同时执行,但最后返回结果是等所有线程执行完成后,再返回,这就是有回调的异步操作:
// 无回调 @Async public CompletableFuturetest() { return CompletableFuture.runAsync(() -> { // 模拟第三方请求加载时间 Thread.sleep(1000); // 异常错误模拟 Integer test = 1/0; System.out.println("异步请求开始执行完成"); }); }
结果就是完全不影响接口执行,且打印出报错信息,如下:
上一篇:MVVM架构