介绍

在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