记spiringboot中@Async注解学习

背景

    最近在学习线程池相关的知识点,发现线程池可以通过execute实现异步方法,然后除了线程池和消息队列能否通过其他方式来实现异步功能。最后通过万能的度娘,发现了spring中的@Async注解,只需要简单的几个步骤就可以实现异步请求,记录学习一下。

使用学习

一、搭建一个springboot 工程并且在启动类中加上@EnableAsync注解

@EnableAsync
@SpringBootApplication
public class AsyncApplication {
    public static void main(String[] args) {
        SpringApplication.run(AsyncApplication.class);
    }
}

二、创建service,里面有两个方法,一个加了@Async注解,一个没加,代码逻辑都差不多

@Slf4j
@Service
public class AsyncServiceImpl implements AsyncService {

    @Async
    public String sayhello() throws InterruptedException, ExecutionException {
        //模拟业务处理,假设需要5s去处理业务
        Thread.sleep(5000);
        ExecutorService newFixedThreadPool = Executors.newSingleThreadExecutor();
        Future<String> submit = newFixedThreadPool.submit(() -> {
            //do someThing
            log.info("invoke sayhello return:Hello!");
            return "Hello!";
        });
        // 调用方调用后会立即返回,所以返回null
        return submit.get();
    }


    public String sayhello2() throws InterruptedException, ExecutionException {
        //模拟业务处理,假设需要5s去处理业务
        Thread.sleep(5000);
        ExecutorService newFixedThreadPool = Executors.newSingleThreadExecutor();
        Future<String> submit = newFixedThreadPool.submit(() -> {
            //do someThing
            log.info("invoke sayhello2 return:Hello2!");
            return "Hello2!";
        });
        // 需要执行完代码才会返回,所以会返回 Hello2!
        return submit.get();
    }
}

三、创建controller类来测试

@Slf4j
@RestController
public class AsyncController {

    @Autowired
    AsyncService asyncService;

    @GetMapping("/sayHello")
    public String sayHello() throws InterruptedException, ExecutionException {
        log.info("请求调用:sayHello");
        String sayhello = asyncService.sayhello();
        log.info("sayHello返回:" + sayhello);
        log.info("============");
        return sayhello;
    }

    @GetMapping("/sayHello2")
    public String sayHello2() throws InterruptedException, ExecutionException {
        log.info("请求调用:sayHello2");
        String sayhello2 = asyncService.sayhello2();
        log.info("sayHello2返回:" + sayhello2);
        log.info("============");
        return sayhello2;
    }
}

(1)使用postman 调用添加了@Async注解的方法

直接返回null,不需要等Thread.sleep(5000)执行完

记spiringboot中@Async注解学习

查看控制台打印的日志, 已经将结果返回给客户端后过了大概5000ms才执行Callable独立线程里面的打印方法

记spiringboot中@Async注解学习

 

 (2)使用postman 调用没有添加@Async注解的方法

postman 等待,表示方法在执行中

记spiringboot中@Async注解学习

 

查看控制台打印的日志,等执行Callable子线程里面的打印方法​​​​​​​以及返回了值之后controller再将值返回给了客户端

记spiringboot中@Async注解学习

 

 再看postman,获取到了值 

记spiringboot中@Async注解学习

 总结:

可以用spring中的@Async注解来做异步处理,相当于在当前线程之外创建一个独立线程,可以简略的当做消息队列来做异步处理。需要注意的是,如果添加该注解的方法的返回类型是Future<>( Future表示一个可能还没有完成的异步任务的结果,针对这个结果可以添加Callback以便在任务执行成功或失败后作出相应的操作) 会开启新的线程执行任务,并阻塞主线程,执行完成后把结果返回给主线程。spring中@Async默认异步配置使用的是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,在压测情况下,会有大量写库请求进入日志写库服务,这时就会不断创建大量线程,极有可能压爆服务器内存,建议使用自定义的线程池。

@Async注解失效的几种情况

1、启动类是否开启异步服务;

2、在定义异步方法的同一个类中,调用带有@Async注解方法,该方法则无法异步执行;

3、注解的方法必须是public方法,不能是static; 

4、没有走Spring的代理类。因为@Transactional和@Async注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器管理。
 

上一篇:Systemstate Dump分析经典案例


下一篇:Systemstate Dump分析经典案例