springboot中为我们提供了@Async注解,查看下源码
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Async {
String value() default "";
}
我们发现,@Async是一个类级别和方法级别的注解,当作用在方法上时,相当于这个方法重新开辟了单独的线程。什么时候我们需要用到异步呢?假如我们在京东上购物完成后付款时,需要对数据进行操作,需要我们等待数据操作成功后,才能完成跳转页面,所以在我们等待的时候,页面处于不可操作的状态,显然不是我们想要的情况。所以这里就需要用到异步了,让操作数据的方法单独开辟一个线程,独立运行,而不影响前台页面的跳转。
接下来我们来写个小案例让大家认识下@Async
service层
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;
import java.util.Date;
import java.util.concurrent.Future;
@Service
public class AsyncService {
//无返回值
public void NoReturn(){
try{
Thread.sleep(3000);//3秒
System.out.println("方法执行结束"+new Date());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
Controller层
@RestController
public class controller {
@Autowired
private AsyncService asyncService;
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String test2(){
long start=System.currentTimeMillis();
asyncService.NoReturn();
return String.format("任务执行成功,耗时{%s}",System.currentTimeMillis() - start);
}
}
在浏览器中输入:http://localhost:8080/hello。
返回结果:
接下来在service层添加@Async
@Service
public class AsyncService {
@Async
public void NoReturn(){
try{
Thread.sleep(3000);
System.out.println("方法执行结束"+new Date());
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
然后在启动类开启异步请求
@EnableAsync//开启异步任务
@SpringBootApplication
public class Springboot5Application {
public static void main(String[] args) {
SpringApplication.run(Springboot5Application.class, args);
}
}
启动项目,在浏览器中输入:http://localhost:8080/hello
查看结果
然后看控制台的输出
有返回值的异步
service层
@Service
public class AsyncService {
@Async
public void NoReturn(){
try{
Thread.sleep(3000);
System.out.println("方法执行结束"+new Date());
}catch (InterruptedException e){
e.printStackTrace();
}
}
@Async
public Future<String> doReturn(int i){
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return new AsyncResult<>(String.format("这个是第{%s}个异步调用的证书",i));
}
}
controller层
@RestController
public class controller {
/**
* 异步
*/
@Autowired
private AsyncService asyncService;
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String test2(){
long start=System.currentTimeMillis();
asyncService.NoReturn();
System.out.println(new Date());
return String.format("任务执行成功,耗时{%s}",System.currentTimeMillis()-start);
}
@GetMapping("/hi")
public Map<String,Object> testAsyncReturn()throws ExecutionException, InterruptedException {
long start=System.currentTimeMillis();
Map<String,Object> map=new HashMap<>();
List<Future<String>> futures=new ArrayList<>();
for (int i=0;i<10;i++){
Future<String> future =asyncService.doReturn(i);
futures.add(future);
}
List<String> response =new ArrayList<>();
for (Future future : futures){
String string =(String) future.get();
response.add(string);
}
map.put("data",response);
map.put("消耗时间",String.format("任务完成,耗时{%s}毫秒",System.currentTimeMillis()-start));
return map;
}
}
启动项目,在浏览器中输入:http://localhost:8080/hi
查看结果
{"data":["这个是第{0}个异步调用的证书","这个是第{1}个异步
调用的证书","这个是第{2}个异步调用的证书","这个是第{3}个异步调用的证书","这个是第{4}个异步调用的证书","这个是第{5}个异步调用的证书","这个是第{6}个异步调用的证书","这个是第{7}个异步调用的证书","这个是第{8}个异步调用的证书","这个是第{9}个异步调用的证书"],"消耗时间":"任务完成,耗时{1004}毫秒"}
注意:一定要批量读取结果,否则达不到异步的效果。
注解扫描时,要注意过滤,避免重复实例化,因为存在覆盖问题,@Async就失效了