详解springboot使用异步注解@Async获取执行结果的坑

网友投稿 1418 2022-12-16

详解springboot使用异步注解@Async获取执行结果的坑

详解springboot使用异步注解@Async获取执行结果的坑

目录一、引言二、获取异步执行结果1、环境介绍2、错误的方式3、正确方式三、异步执行@Async注解四、总结

一、引言

java后端开发中经常会碰到处理多个任务的情况,比如一个方法中要调用多个请求,然后把多个请求的结果合并后统一返回,一般情况下调用其他的请求一般都是同步的,也就是每个请求都是阻塞的,那么这个处理时间必定是很长的,有没有一种方法可以让多个请求异步处理那,答案是有的。

springboot中提供了很便利的方式可以解决上面的问题,那就是异步注解@Async。正确的使用该注解可以使你的程序飞起,相反如果使用不当那么并不会取到理想的效果。

二、获取异步执行结果

1、环境介绍

下面是我的controller,SyncController.java

package com.atssg.controller;

import com.atssg.service.MySyncService;

import lombok.extern.slf4j.Slf4j;

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

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

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

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

@Slf4j

@RestController

@RequestMapping("/sync")

public class SyncController {

@Autowired

private MySyncService syncService;

@GetMapping(value = "/test")

public String test() {

String str=null;

try {

log.info("start");

str = syncService.asyncMethod();

log.info("str:{}", str);

return str;

} catch (Exception e) {

e.printStackTrace();

}

return str;

}

}

在controller中就是调用下层的方法并返回,再看service层的类MySyncService.java

package com.atssg.service;

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

import org.springframework.stereotype.Service;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.Future;

@Service

public class MySyncService {

@Autowired

private SyncService syncService;

/**

* 异步方法

*

* @return

* @throws InterruptedException

* @throws ExecutionException

*/

public String asyncMethod() throws InterruptedException, ExecutionException {

Future result1 = syncService.method1("I");

Future result2 = syncService.method2("love");

Future result3 = syncService.method3("async");

String str = result1.get();

String str2 = result2.get();

String str3 = result3.get();

String result = str + str2 + str3;

return result;

}

/**

* 同步方法

*

* @return

* @throws InterruptedException

* @throws ExecutionException

*/

public String syncMethod() throws InterruptedException, ExecutionException {

/*同步写法*/

String str = syncService.method1("I").get();

String str2 = syncService.method2("love").get();

String str3 = syncService.method3("async").get();

return str + str2 + str3;

}

}

上面便是service类,仅仅是调用下次异步层的方法,并取得返回值。上面类中有两个方法,其写法也类似但结果却大不相同,后面详说。

下面是异步层的方法,SyncService.java

package com.atssg.service;

import org.springframework.scheduling.annotation.Async;

import org.springframework.scheduling.annotation.AsyncResult;

import org.springframework.stereotype.Service;

import java.util.concurrent.Future;

@Service

@Async

public class SyncService {

//@Async

public Future method1(String str) throws InterruptedException {

Thread.sleep(1000*10);

return new AsyncResult<>( str);

}

//@Async

public Future method2(String str) throws InterruptedException {

Thread.sleep(1000*5);

return new AsyncResult<>(str);

}

// @Async

public Future method3(String str) throws InterruptedException {

Thread.sleep(1000*15);

return new AsyncResult<>(str);

}

}

该类使用@Async注解,表明该类中所有的方法都是异步执行的,其中@Async可修饰类也可以修饰方法。

这便是所有的环境。

2、错误的方式

在MySyncService中有两个方法,先看其中一个方法

public String syncMethod() throws InterruptedException, ExecutionException {

/*同步写法*/

String str = syncService.method1("I").get();

String str2 = syncService.method2("love").get();

String str3 = syncService.method3("async").get();

return str + str2 + str3;

}

这种写法是调用异步方法后立即调用get()方法,即获取结果,下面看测试结果,在controllor中调用该方法,下面看执行结果

2021-08-21 11:06:28.612  INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController      : start

2021-08-21 11:06:58.651  INFO 3584 --- [nio-8080-exec-1] com.atssg.controller.SyncController      : str:Iloveasync

可以看到共执行了30s,在异步层的方法中的三个方法如下,

//@Async

public Future method1(String str) throws InterruptedException {

Thread.sleep(1000*10);

return new AsyncResult<>( str);

}

//@Async

public Future method2(String str) throws InterruptedException {

Thread.sleep(1000*5);

return new AsyncResult<>(str);

}

// @Async

public Future method3(String str) throws InterruptedException {

Thread.sleep(1000*15);

return new AsyncResult<>(str);

}

可以看到这三个方法分别是睡眠10s、5s、15s,这就很好理解了syncMethod()方法中的写法是同步的,未达到异步的目的,切记调用完异步方法进接着调用get()方法不是异步的方式,而是同步的。

3、正确方式

上面看了错误的用法,下面看正确的方式,

public String asyncMethod() throws InterruptedException, ExecutionException {

Future result1 = syncService.method1("I");

Future result2 = syncService.method2("love");

Future result3 = syncService.method3("async");

String str = result1.get();

String str2 = result2.get();

String str3 = result3.get();

String result = str + str2 + str3;

return result;

}

这种方式是首先调用异步方法,然后分别调用get()方法,取得执行结果。下面看测试结果

2021-08-21 11:17:23.516  INFO 3248http:// --- [nio-8080-exec-1] com.atssg.controller.SyncController      : start

2021-08-21 11:17:38.535  INFO 3248 --- [nio-8080-exec-1] com.atssg.controller.SyncController      : str:Iloveasync

执行时间未15s,这就很好解释了,异步层的三个方法,分别睡眠的时间是10s、5s、15s,既然是异步执行的,那么总的执行时间肯定是三个方法中最长的那个,符合测试结果。这才@Async正确的打开姿势。

三、异步执行@Async注解

@Async注解的定义如下,

@Target({ElementType.TYPE, ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface Async {

String value() default "";

}

可以看到该注解可以用在类及方法上,用在类上表示类中的所有方法都是异步的,用在方法上表示该方法是异步的。

四、总结

今天的文章分享到这里,主要分享了关于@Async注解在获取执行结果的时候的坑,一定要先调用异步方法,然后再调用get()方法,获取结果,其中get方法还有一个重载的,可以设置超时时间,即超过设置的超时时间便返回,不再等待,各位小伙伴可以自己试验。

V get(long timeout, TimeUnit unit)

throws InterruptedException, ExecutionException, TimeoutException;

}

下次继续分享有关@Async注解使用的一些小细节,欢迎持续关注。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Springsecurity Oauth2如何设置token的过期时间
下一篇:使用springCloud+nacos集成seata1.3.0搭建过程
相关文章

 发表评论

暂时没有评论,来抢沙发吧~