Springboot @Async多线程获取返回值方式

 更新时间:2023年09月27日 09:21:17   作者:小白Alan  
这篇文章主要介绍了Springboot @Async多线程获取返回值方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

@Async 多线程获取返回值

最近需要用到多线程 自己维护线程池很麻烦 正好看到 springboot 集成线程池的例子 这里自己做了个尝试和总结 记录一下 也分享给需要的朋友;

不考虑事务的情况下 这个多线程实现比较简单 

主要有以下几点

  • 在启动类加上  @EnableAsync 注解, 开启异步执行支持;
  • 编写线程池配置类, 别忘了 @Configuration , 和 @Bean 注解;
  • 编写需要异步执行的业务, 放到单独的类中 (可以定义为 service, 因为需要 spring 管理起来才能用 );
  • 在业务service中调用异步执行的service, 注意这是重点, 不能直接在业务 service 中写异步执行的代码, 否则无法异步执行( 这就是单独放异步代码的原因);

好了, 上代码:

// 启动类
@EnableAsync
@EnableWebSecurity
@ServletComponentScan
@SpringBootApplication(scanBasePackages={"com.example.demo"})
public class DemoApplication {
	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}
// 线程池配置类
@Slf4j
@Configuration
public class BootThreadpoolConfig {
    // 配置核心线程数
    private int corePoolSize = 5;
    // 配置最大线程数
    private int maxPoolSize = 20;
    // 配置任务队列的长度
    private int queueCapacity = 500;
    // 配置任务的空闲时间
    private int aliveSeconds = 600;
    // 配置线程前缀
    private String namePrefix = "localThreadPool";
    // 自定义线程池, 起个好记的名
    @Bean(name = "localBootAsyncExecutor")
    public Executor asyncServiceExecutor() {
        log.info("初始化 springboot 线程池");
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //配置核心线程数
        executor.setCorePoolSize(corePoolSize);
        //配置最大线程数
        executor.setMaxPoolSize(maxPoolSize);
        //配置队列大小
        executor.setQueueCapacity(queueCapacity);
        //配置线程池中的线程的名称前缀
        executor.setThreadNamePrefix(namePrefix);
        //配置线程的空闲时间
        executor.setKeepAliveSeconds(aliveSeconds);
        // RejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务
        // CallerRunsPolicy:不在新线程中执行任务,而是有调用者所在的线程来执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        //执行初始化
        executor.initialize();
        log.info("springboot 线程池初始化完毕");
        return executor;
    }
}
// 异步执行代码
@Service("asyncExecutorTest")
public class AsyncExecutorTest {
    // 异步执行的方法, 注解内为自定义线程池类名
    @Async("localBootAsyncExecutor")
    public Future<Integer> test1(Integer i) throws InterruptedException {
        Thread.sleep(100);
        System.out.println("@Async 执行: " + i);
        return new AsyncResult(i);
    }
    // 这里使用其它方式调用,详见后面的 service3 方法
    public Integer test2(Integer i) throws InterruptedException {
        Thread.sleep(100);
        System.out.println(" excute.run 执行: " + i);
        return i;
    }
}
// 业务 service
@Service("asyncExcutorService")
public class AsyncExcutorService {
    @Autowired
    AsyncExecutorTest asyncExecutorTest;
    @Autowired
    Executor localBootAsyncExecutor;
    // 测试 无返回值异步执行
    public void service1(){
        System.out.println("service1 执行----->");
        for (int i = 0; i < 50; i++) {
            try {
                asyncExecutorTest.test1(i);
            } catch (InterruptedException e) {
                System.out.println("service1执行出错");
            }
        }
        System.out.println("service1 结束----->");
    }
    // 测试 有返回值异步执行
    public void service2(){
        long l = System.currentTimeMillis();
        System.out.println("service2 执行----->");
        List<Future> result = new ArrayList<>();
        try {
            for (int i = 0; i < 300; i++) {
                Future<Integer> integerFuture = asyncExecutorTest.test1(i);
                result.add(integerFuture);
            }
            for (Future future : result) {
                System.out.println(future.get());
            }
        } catch (InterruptedException | ExecutionException e) {
            System.out.println("service2执行出错");
        }
        System.out.println("service2 结束----->" + (System.currentTimeMillis() - l));
    }
    // 测试 有返回值异步执行
    public void service3(){
        long l = System.currentTimeMillis();
        List<Integer> result = new ArrayList<>();
        try {
            System.out.println("service3 执行----->");
            int total = 300;
            CountDownLatch latch = new CountDownLatch(total);
            for (int i = 0; i < total; i++) {
                final int y = i;
                localBootAsyncExecutor.execute(() -> {
                    try {
                        result.add(asyncExecutorTest.test2(y));
                    } catch (InterruptedException e) {
                        System.out.println("service3执行出错");
                    } finally {
                        latch.countDown();
                    }
                });
            }
            latch.await();
        } catch (InterruptedException e) {
            System.out.println("service3执行出错");
        }
        System.out.println("service3 结束----->" + (System.currentTimeMillis() - l));
    }
}

这里说下service1 和 service2的区别

1. 两个都用的是一个线程池执行的

2. service1 单纯执行业务, 不用返回数据, 主线程也不用等待

3. service2 需要返回数据, 主线程需要等待结果( 注意返回值只能是 Future, 最后再 .get()去获取, 否则无法异步执行)

4. service3 也可以返回数据, 但是书写上麻烦一些.  返回值直接是想要的结果, 不像 service2 还需要提取一次数据.

其它就是 controller 了, 自己随便写一个调用一下就行了, 这里就不写了, 以下是我测试的 console 日志

  • service1 执行日志

  • service2 执行日志

  • service3

总结

打完收工!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • spring中使用@Autowired注解无法注入的情况及解决

    spring中使用@Autowired注解无法注入的情况及解决

    这篇文章主要介绍了spring中使用@Autowired注解无法注入的情况及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • Java8 stream流分组groupingBy的使用方法代码

    Java8 stream流分组groupingBy的使用方法代码

    对于java8的新特性groupingBy方法,相信有很多人都在工作中用过,这篇文章主要给大家介绍了关于Java8 stream流分组groupingBy的使用方法,需要的朋友可以参考下
    2024-01-01
  • springboot 如何设置端口号和添加项目名

    springboot 如何设置端口号和添加项目名

    这篇文章主要介绍了springboot设置端口号和添加项目名的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • JSON中fastjson、jackson、gson如何选择

    JSON中fastjson、jackson、gson如何选择

    在Java中,JSON的解析方式很多,例如fastjson(阿里)、Gson(谷歌)、jackjson等,本文主要介绍了JSON中fastjson、jackson、gson如何选择,具有一定的参考价值,感兴趣的可以了解一下
    2021-12-12
  • springboot2.6.4集成swagger3.0遇到的坑及解决方法

    springboot2.6.4集成swagger3.0遇到的坑及解决方法

    这篇文章主要介绍了springboot2.6.4如何集成swagger3.0,在集成的过程中遇到很多问题,本文给大家分享四种问题及相应的解决方案,需要的朋友可以参考下
    2022-03-03
  • java中重写父类方法加不加@Override详解

    java中重写父类方法加不加@Override详解

    这篇文章主要介绍了java中重写父类方法加不加@Override详解,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Java新特性中Preview功能如何运行调试详解

    Java新特性中Preview功能如何运行调试详解

    这篇文章主要为大家介绍了Java新特性中Preview功能如何运行调试详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Java字符串 正则表达式详解

    Java字符串 正则表达式详解

    这篇文章主要介绍了java使用正则表达式查找包含的字符串功能,结合具体实例形式分析了java针对字符串匹配查找的相关实现技巧,需要的朋友可以参考下
    2021-09-09
  • java实现微信企业付款到个人

    java实现微信企业付款到个人

    这篇文章主要为大家详细介绍了java实现微信企业付款到个人功能,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • 一文详解Lombok中@ToString()的使用技巧

    一文详解Lombok中@ToString()的使用技巧

    在平时我们工作的时候,我们经常会使用toString() 方法来输出一个对象的一些属性信息。Lombok 给我们提供了一个自动生成 toString()代码的注解,可以减少代码行数,本文就来和大家详细聊聊吧
    2023-02-02

最新评论