欢迎来到淼淼之森的博客小站。  交流请加我微信好友: studyjava。  也欢迎关注同名公众号:Java学习之道

SpringBoot使用@Async注解的默认线程池问题 置顶!

  |   0 评论   |   0 浏览

@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor。默认核心线程数:8,最大线程数:Integet.MAX_VALUE,队列使用LinkedBlockingQueue,容量是:Integet.MAX_VALUE,空闲线程保留时间:60s,线程池拒绝策略:AbortPolicy。

@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor(参考TaskExecutionAutoConfiguration),

其中默认核心线程数为8, 默认最大队列和默认最大线程数都是Integer.MAX_VALUE. 创建新线程的条件是队列填满时, 而这样的配置队列永远不会填满;如果我们不自定义线程池,当在大量请求下,就可能会发生 OOM 问题!!!或者说由于我们队列可以认为是无界的,可以无限制添加任务,这就导致内存占用越来越高,直到 OOM 爆仓。

如果有@Async注解标注的方法长期占用线程(比如HTTP长连接等待获取结果),在核心8个线程数占用满了之后, 新的调用就会进入队列, 外部表现为没有执行。

可以手动配置相应属性:

#核心线程数
spring.task.execution.pool.core-size=200
#最大线程数
spring.task.execution.pool.max-size=1000
#空闲线程保留时间
spring.task.execution.pool.keep-alive=3s
#队列容量
spring.task.execution.pool.queue-capacity=1000
#线程名称前缀
spring.task.execution.thread-name-prefix=test-thread-

配置类是TaskExecutionProperties【org.springframework.boot.autoconfigure.task.TaskExecutionProperties】

现象:

    1. 表面现象: 方法中输出的日志, 日志文件中找不到, 也没有任何报错(即@Async标注的方法没有执行, 也没有报错)
    1. 分析现象: 日志中某个时刻之后没有了task-xxx线程的日志

原因:

@Async异步方法默认使用Spring创建ThreadPoolTaskExecutor(参考TaskExecutionAutoConfiguration),

其中默认核心线程数为8, 默认最大队列和默认最大线程数都是Integer.MAX_VALUE. 创建新线程的条件是队列填满时, 而

这样的配置队列永远不会填满, 如果有@Async注解标注的方法长期占用线程(比如HTTP长连接等待获取结果),

在核心8个线程数占用满了之后, 新的调用就会进入队列, 外部表现为没有执行.

解决:

1、配置文件配置

手动配置相应属性即可. 比如

#核心线程数
spring.task.execution.pool.core-size=20
#最大线程数
spring.task.execution.pool.max-size=100
#空闲线程保留时间
spring.task.execution.pool.keep-alive=10s
#队列容量
spring.task.execution.pool.queue-capacity=100
#线程名称前缀
spring.task.execution.thread-name-prefix=test-thread-

2、代码配置

如果使用注解配置,我们需要设置一个 Bean:

@Configuration
@EnableAsync
public class TaskExecutorConfig {

    public static final String EXECUTOR_SERVICE = "asyncExecutor";

    @Bean(EXECUTOR_SERVICE)
    public TaskExecutor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置核心线程数
        executor.setCorePoolSize(Runtime.getRuntime().availableProcessors());
        // 设置最大线程数
        executor.setMaxPoolSize(Runtime.getRuntime().availableProcessors() * 10);
        // 设置队列容量
        executor.setQueueCapacity(Runtime.getRuntime().availableProcessors() * 10);
        // 设置线程活跃时间(秒)
        executor.setKeepAliveSeconds(10);
        // 设置默认线程名称
        executor.setThreadNamePrefix("async-");
        // 设置拒绝策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 等待所有任务结束后再关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        return executor;
    }
}

然后使用注解时指定线程池名称:

@Async(TaskExecutorConfig.EXECUTOR_SERVICE)
public void xx() {
	// 业务逻辑
}

总结一句

由于@Async注解默认使用 LinkedBlockingQueue 队列,这个队列如果我们不指定队列数量,默认数量上限为 Integer.MAX_VALUE。如果线程池执行任务过慢,任务将会一直堆积在队列中。由于我们队列可以认为是无界的,可以无限制添加任务,这就导致内存占用越来越高,直到 OOM 爆仓。因此调整线程池的默认配置就相当有必要了!


标题:SpringBoot使用@Async注解的默认线程池问题
作者:mmzsblog
地址:https://www.mmzsblog.cn/articles/2022/03/26/1648264017665.html

如未加特殊说明,文章均为原创,转载必须注明出处。均采用CC BY-SA 4.0 协议

本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。若本站转载文章遗漏了原文链接,请及时告知,我们将做删除处理!文章观点不代表本网站立场,如需处理请联系首页客服。
• 网站转载须在文章起始位置标注作者及原文连接,否则保留追究法律责任的权利。
• 公众号转载请联系网站首页的微信号申请白名单!

个人微信公众号 ↓↓↓                 

微信搜一搜 Java 学习之道