SpringBoot中的异步多线程使用及避坑指南

本文介绍了如何在SpringBoot中使用@Async注解实现异步方法处理,包括配置ThreadPoolTaskExecutor,使用CompletableFuture进行多结果聚合,并指出@Async注解失效的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SpringBoot中的异步多线程使用及避坑指南
处理请求时需要考虑到系统的性能和响应速度。特别是在处理大量请求或者需要进行耗时操作时,采用异步多线程处理是一种常见的解决方案。Spring Boot提供了@Async注解来支持异步方法调用,结合合适的线程池配置,可以很容易地实现异步多线程处理,提升系统的并发能力和性能。

1.配置线程池

@Configuration
@EnableAsync
public class AsyncConfiguration {
    @Bean("doSomethingExecutor")
    public Executor doSomethingExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);
        // 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(20);
        // 缓冲队列:用来缓冲执行任务的队列大小
        executor.setQueueCapacity(500);
        // 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("do-something-");
          // 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardPolicy());
        executor.initialize();
        return executor;
    }
}

在这个配置中,我们使用了ThreadPoolTaskExecutor作为线程池的实现,并且设置了一些关键参数,如核心线程数、最大线程数、缓冲队列大小等。如果不太了解线程池的小伙伴可以看一下之前介绍线程池介绍线程池的核心参数,线程池的执行原理知道

  1. @Async注解
    在需要异步执行的方法上使用@Async注解。这样的方法将会在一个单独的线程中执行,而不会阻塞主线程。
@Slf4j
@Service
public class AsyncService {
   // 指定使用beanname为doSomethingExecutor的线程池
    @Async("doSomethingExecutor")
    public  CompletableFuture<String> doSomething(String message) throws InterruptedException {
        log.info("doSomethingExecutor thread name  ={}", Thread.currentThread().getName());
        Thread.sleep(1000);
        return CompletableFuture.completedFuture(message);
    }
}

doSomething()方法被标记为异步方法,并且指定了使用名为"doSomethingExecutor"的线程池进行执行。

  1. 异步多结果聚合返回CompletableFuture
    在某些情况下,我们可能需要等待多个异步任务执行完毕后再进行下一步操作,这时可以使用CompletableFuture来实现异步多结果的聚合。
@RestController
@RequestMapping
public class AsyncController {

    @Autowired
    private AsyncService asyncService;


    @GetMapping("/open/somethings")
    public List<String> somethings() throws InterruptedException {
        int count = 6;
        List<CompletableFuture<String>> futures = new ArrayList<>();
        List<String> results = new ArrayList<>();
        // 启动多个异步任务,并将 CompletableFuture 对象存储在列表中
        for (int i = 1; i < count; i++) {
            CompletableFuture<String> future = asyncService.doSomething("index: "+i);
            futures.add(future);
        }
        
        for (CompletableFuture<String> future : futures) {
            String result = future.get(); // 阻塞等待异步任务完成并获取结果
            results.add(result);
        }
        return results;
    }
}

我们通过循环启动了多个异步任务,将返回的 CompletableFuture 对象存储在列表中。然后,我们再次循环遍历这些 CompletableFuture 对象,并调用 get() 方法来阻塞等待异步任务完成,获取结果。最后,将结果添加到结果列表中并返回

使用浏览器发送http://localhost:8888/open/somethings,结果如下
在这里插入图片描述
@Async注解会在以下几个场景失效,使用了@Async注解,但就没有走多线程:

异步方法使用static关键词修饰;
异步类不是一个Spring容器的bean(一般使用注解@Component和@Service,并且能被Spring扫描到);
SpringBoot应用中没有添加@EnableAsync注解;
在同一个类中,一个方法调用另外一个有@Async注解的方法,注解不会生效。原因是@Async注解的方法,是在代理类中执行的。
异步方法使用注解@Async的返回值只能为void或者Future及其子类,当返回结果为其他类型时,方法还是会异步执行,但是返回值都是null

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

princeAladdin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值