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

总结

打完收工!

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

相关文章

  • Elasticsearch8.1中的Script使用实例深入解读

    Elasticsearch8.1中的Script使用实例深入解读

    这篇文章主要为大家介绍了Elasticsearch8.1中的Script使用实例深入解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • Intellij IDEA配置Jetty的方法示例

    Intellij IDEA配置Jetty的方法示例

    这篇文章主要介绍了Intellij IDEA配置Jetty的方法示例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-10-10
  • PowerJob的Evaluator方法工作流程源码解读

    PowerJob的Evaluator方法工作流程源码解读

    这篇文章主要介绍了PowerJob的Evaluator方法工作流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • 详解Spring中Bean的加载的方法

    详解Spring中Bean的加载的方法

    本篇文章主要介绍了Spring中Bean的加载的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • Spring打包jar包时jsp页面无法访问问题解决

    Spring打包jar包时jsp页面无法访问问题解决

    这篇文章主要介绍了Spring打包jar包时jsp页面无法访问问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-05-05
  • 浅析NIO系列之TCP

    浅析NIO系列之TCP

    NIO即同步非阻塞式IO,它和传统的BIO比较最大的区别在于在执行accept、connect、read、write操作时是非阻塞的。很有利于实现用少量线程来处理多个客户端请求,可以随时让线程切换所处理的客户端,从而可以实现高并发服务器的开发
    2021-06-06
  • 关于spring中单例Bean引用原型Bean产生的问题及解决

    关于spring中单例Bean引用原型Bean产生的问题及解决

    这篇文章主要介绍了关于spring中单例Bean引用原型Bean产生的问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-06-06
  • 了解Java线程池执行原理

    了解Java线程池执行原理

    那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?在Java中可以通过线程池来达到这样的效果。下面我们来详细了解一下吧
    2019-05-05
  • springboot配置mongodb连接池的方法步骤

    springboot配置mongodb连接池的方法步骤

    这篇文章主要介绍了springboot配置mongodb连接池的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-01-01
  • java String 可变性的分析

    java String 可变性的分析

    这篇文章主要介绍了java String 可变性的分析的相关资料,通常大家都认为java String 是不可变的,这里分析下源码来说明它的可变性,需要的朋友可以参考下
    2017-03-03

最新评论