欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

SpringBoot中@Async的用法与实践

程序员文章站 2022-10-03 19:21:22
介绍在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务。其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法。@Async的使用注意事项必须用在public方法上,且不能是static方法不能与调用的方法在同一个类中需要把该方法注入到Spring容器中,就是在一个类中添加异步方法,并在此类上使用@Component之类的注...

介绍

在Java应用中,绝大多数情况下都是通过同步的方式来实现交互处理的;但是在处理与第三方系统交互的时候,容易造成响应迟缓的情况,之前大部分都是使用多线程来完成此类任务。其实,在Spring 3.x之后,就已经内置了@Async来完美解决这个问题,本文将完成介绍@Async的用法。

@Async的使用

注意事项

  • 必须用在public方法上,且不能是static方法
  • 不能与调用的方法在同一个类中
  • 需要把该方法注入到Spring容器中,就是在一个类中添加异步方法,并在此类上使用@Component之类的注解加入到容器

注解配置开启

在springboot启动类上添加 @EnableAsync 注解

@EnableAsync
public class CupidApplication {

基于@Async无返回值调用

/**
     * 没有返回值的Async方法
     */
    @Override
    @Async
    public void asyncMethodWithVoidReturnType() {
        log.info("没有返回值的Async方法, ThreadName : {}", Thread.currentThread().getName());
    }

使用的方式非常简单,一个标注即可解决所有的问题。

调用结果:

2020-08-27 11:52:31.197  INFO 7311 --- [         task-5] c.w.cupid.service.impl.AsyncServiceImpl  : 没有返回值的Async方法, ThreadName : task-5

基于@Async返回值的调用

/**
     * 有返回值的Async方法
     * @return    Future    new AsyncResult
     */
    @Override
    @Async
    public Future<String> asyncMethodWithReturnType() {
        log.info("有返回值的Async方法, ThreadName : {}", Thread.currentThread().getName());
        try {
            Thread.sleep(5000);
            return new AsyncResult<String>("hello world !!!!");
        } catch (InterruptedException e) {
            log.error("出问题了, {}", e.getMessage());
        }
        return null;
    }

以上示例可以发现,返回的数据类型为Future类型,其为一个接口。具体的结果类型为AsyncResult,这个是需要注意的地方。

调用返回结果的异步方法示例:

@GetMapping("/future")
    public String futureAsync() throws ExecutionException, InterruptedException {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("Invoking an asynchronous method. threadName : " + Thread.currentThread().getName());

        Future<String> stringFuture = asyncService.asyncMethodWithReturnType();
        while (true) {
            if (stringFuture.isDone()) {
                stringBuilder.append("Result from asynchronous process - " + stringFuture.get());
                break;
            }
            stringBuilder.append("Continue doing something else.");
            Thread.sleep(1000);
        }
        return stringBuilder.toString();
    }

分析: 这些获取异步方法的结果信息,是通过不停的检查Future的状态来获取当前的异步方法是否执行完毕来实现的。

给@Async使用自定义的线程池

@Configuration
public class ThreadPoolConfig {

    /**
     * logger
     */
    private final static Logger logger = LoggerFactory.getLogger(ThreadPoolConfig.class);

    @Value("${asyncThreadPool.corePoolSize}")
    private int corePoolSize;

    @Value("${asyncThreadPool.maxPoolSize}")
    private int maxPoolSize;

    @Value("${asyncThreadPool.queueCapacity}")
    private int queueCapacity;

    @Value("${asyncThreadPool.keepAliveSeconds}")
    private int keepAliveSeconds;

    @Value("${asyncThreadPool.awaitTerminationSeconds}")
    private int awaitTerminationSeconds;

    @Value("${asyncThreadPool.threadNamePrefix}")
    private String threadNamePrefix;

    /**
     * 线程池配置
     * @param
     * @return java.util.concurrent.Executor
     * @author wliduo
     * @date 2019/2/15 14:44
     */
    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
        logger.info("---------- 线程池开始加载 ----------");
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        // 核心线程池大小
        threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
        // 最大线程数
        threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
        // 队列容量
        threadPoolTaskExecutor.setQueueCapacity(keepAliveSeconds);
        // 活跃时间
        threadPoolTaskExecutor.setKeepAliveSeconds(queueCapacity);
        // 主线程等待子线程执行时间
        threadPoolTaskExecutor.setAwaitTerminationSeconds(awaitTerminationSeconds);
        // 线程名字前缀
        threadPoolTaskExecutor.setThreadNamePrefix(threadNamePrefix);
        // RejectedExecutionHandler:当pool已经达到max-size的时候,如何处理新任务
        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 初始化
        threadPoolTaskExecutor.initialize();
        logger.info("---------- 线程池加载完成 ----------");
        return threadPoolTaskExecutor;
    }
}

使用自定义的线程池

@Async(“threadPoolTaskExecutor”)

只需要在 @Async 注解的括号里加上自定义的线程池的bean即可

@Async调用中的事务处理机制

在@Async标注的方法,同时也适用了@Transactional进行了标注;在其调用数据库操作之时,将无法产生事务管理的控制,原因就在于其是基于异步处理的操作。

那该如何给这些操作添加事务管理呢?可以将需要事务管理操作的方法放置到异步方法内部,在内部被调用的方法上添加@Transactional。

例如:

方法A,使用了@Async/@Transactional来标注,但是无法产生事务控制的目的。

方法B,使用了@Async来标注,B中调用了C、D。C/D分别使用@Transactional做了标注,则可实现事务控制的目的。

参考资料

https://www.jianshu.com/p/0e61693860b7?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends

https://blog.csdn.net/zhuyu19911016520/article/details/99992668

https://www.jianshu.com/p/49b9d15456d9?utm_campaign=hugo&utm_medium=reader_share&utm_content=note&utm_source=weixin-friends

本文地址:https://blog.csdn.net/MCmango/article/details/113995203