SpringBoot 2.X中的@Async和Java8中的completableFuture的使用比较

时间:2022-07-23
本文章向大家介绍SpringBoot 2.X中的@Async和Java8中的completableFuture的使用比较,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

背景

看到項目中有使用到Async注解和completetableFuture的runApply方法的使用。兩者都是異步提交方法的方式。那他两都分别在什么场景底下比较适用呢?

  1. 非常的明显的区别,一个是注解一个是方法调用。这样的话带来利好了。注解注释某个方法,那这个方法只要被调用就会产生异步。那使用的completableFuture的话你调用那个方法那个方法才会被异步。
  2. Async 产生的默认使用的线程池是不一样的。一个是forkJoinPool 一个是AsyncTaskExecutor。
  3. 两个都是用默认性能和产生的默认的线程数

@Async简介

为了使得异步可用,Spring提供了一个注解@EnableAsync如果Java的配置文件标注他,那么Spring就会开启同步可用,这样就可以使用注解@Async驱动Spring使用的异步调用,其中的默认线程池也就是AsyncTaskExecutor,默认参数为无限大(首先简单百度了下,网上提到@Async默认异步配置使用的是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,在大量的请求的时候,这时就会不断创建大量线程,极有可能压爆服务器内存。如下面的测试情况,无限创建线程) 使用Java配置demo:

package com springboot chapterl3.config
/**** imports ******/
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer { 
//定义线程池
Override
public Executor getAsyncExecutor () { 
//定义线程池
ThreadPoolTaskExecutor taskExecutor =new ThreadPoolTaskExecutor(); 
//核心线程数
taskExecutor setCorePool ze (10); 
//线程池最大线程数
taskExecutor setMaxPoolS ze (30) ; 
//线程队列最大线程数
taskExecutor . setQueueCapacity(2000) ; 
//初始化
taskExecutor.initialize(); 
return taskExecutor;
    }
}

CompleteTableTuture简介

点击:https://blog.csdn.net/weixin_40413961/article/details/107703172

两者demo压测比较

使用CompleteTableTuture demo

/**
 * @author yuanxindong
 * @date: 2020/7/31 16:53
 */
@Service
public class AsyncTest {
    @Autowired
    AsycMethodDemo asycMethodDemo;

    @Test
    public void runAsyncTask() throws InterruptedException {
        Thread.sleep(50000);
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        AsycMethodDemo asycMethodDemo = applicationContext.getBean(AsycMethodDemo.class);
        Integer i = 0;
        for (i = 0; i < 100; i++) {
            getTask(asycMethodDemo, i);
        }

        Thread.sleep(1000000);
    }

    private void getTask(AsycMethodDemo asycMethodDemo, Integer i) {
        try {
            CompletableFuture.runAsync(() -> {
                try {
                    asycMethodDemo.asyncMethod(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        } catch (Exception e) {

        }
    }

}

运行结果:

除了主线程,根据图看到一共起了8个线程。是的forkJoinPool的默认核心线程数是根据CPU的核数来穿建的

  • 使用Java8中的completableFuture的使用demo
/**
 * @author yuanxindong
 * @date: 2020/7/31 16:53
 */
@Service
public class AsyncTest {
    @Autowired
    AsycMethodDemo asycMethodDemo;
    @Test
   public void runAsyncTask() throws InterruptedException {
        Thread.sleep(50000);
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
        AsycMethodDemo asycMethodDemo =  applicationContext.getBean(AsycMethodDemo.class);
    int i = 0;
    for( i = 0; i< 100;i++){
        asycMethodDemo.asyncMethod(i);
        System.out.println("任务"+i+"执行完成");
    }

    Thread.sleep(1000000);
    }

}

运行结果:

可以看出,一共起了100个线程,由于线程起的多可以明显看到这几个任务执行完成的真的很快,这也就是传说中的时间并行执行吧,但是这里会有 一个问题那就是如果任务真的超级多的话,会不会爆掉。

总结

个人感觉Java 8的completeTable比较好用一些,也支持自定义。

  1. Spring的也是OK的,具体情景具体选择吧
  2. @Async的时候一定要设置线程数,以防万一OOM