springboot为异步任务规划自定义线程池的实现


Posted in Java/Android onJune 14, 2022

一、Spring Boot任务线程池

线程池的作用

  • 防止资源占用无限的扩张
  • 调用过程省去资源的创建和销毁所占用的时间

在高并发环境下,不断的分配新资源,可能导致系统资源耗尽。所以为了避免这个问题,我们为异步任务规划一个线程池。当然,如果没有配置线程池的话,springboot会自动配置一个ThreadPoolTaskExecutor 线程池到bean当中。

# 核心线程数
spring.task.execution.pool.core-size=8  
# 最大线程数
spring.task.execution.pool.max-size=16
# 空闲线程存活时间
spring.task.execution.pool.keep-alive=60s
# 是否允许核心线程超时
spring.task.execution.pool.allow-core-thread-timeout=true
# 线程队列数量
spring.task.execution.pool.queue-capacity=100
# 线程关闭等待
spring.task.execution.shutdown.await-termination=false
spring.task.execution.shutdown.await-termination-period=
# 线程名称前缀
spring.task.execution.thread-name-prefix=task-

在springboot配置文件中加入上面的配置,即可实现ThreadPoolTaskExecutor 线程池。

二、自定义线程池

有的时候,我们希望将系统内的一类任务放到一个线程池,另一类任务放到另外一个线程池,所以使用Spring Boot自带的任务线程池就捉襟见肘了。下面介绍自定义线程池的方法。

创建一个 线程池配置类 TaskConfiguration ,并配置一个 任务线程池对象 taskExecutor。

@Configuration
public class TaskConfiguration {
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(10);
        executor.setMaxPoolSize(20);
        executor.setQueueCapacity(200);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("taskExecutor-");
        executor.setRejectedExecutionHandler(new CallerRunsPolicy());
        return executor;
    }
}

springboot为异步任务规划自定义线程池的实现

上面我们通过使用 ThreadPoolTaskExecutor 创建了一个 线程池,同时设置了以下这些参数:

线程池属性 属性的作用 上文代码设置初始值
核心线程数CorePoolSize 线程池创建时候初始化的线程数,最小线程数 10
最大线程数MaxPoolSize 线程池最大的线程数,只有在缓冲队列满了之后,才会申请超过核心线程数的线程 20
缓冲任务队列QueueCapacity 用来缓冲执行任务的队列 200
允许线程的空闲时间KeepAliveSeconds 超过了核心线程之外的线程,在空闲时间到达之后,没活干的线程会被销毁 60秒
线程池名的前缀 ThreadNamePrefix 可以用于定位处理任务所在的线程池 taskExecutor-
线程池对任务的Reject策略RejectedExecutionHandler 当线程池运行饱和,或者线程池处于shutdown临界状态时,用来拒绝一个任务的执行 CallerRunsPolicy

Reject策略预定义有四种:

  • AbortPolicy,用于被拒绝任务的处理程序,它将抛出RejectedExecutionException。
  • CallerRunsPolicy,用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。
  • DiscardOldestPolicy,用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。
  • DiscardPolicy,用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。

创建 AsyncExecutorTask类,三个任务的配置和 AsyncTask 一样,不同的是 @Async 注解需要指定前面配置的 线程池的名称 taskExecutor。

@Component
public class AsyncExecutorTask extends AbstractTask {
    @Async("taskExecutor")
    public Future<String> doTaskOneCallback() throws Exception {
        super.doTaskOne();
        System.out.println("任务一,当前线程:" + Thread.currentThread().getName());
        return new AsyncResult<>("任务一完成");
    }

    @Async("taskExecutor")
    public Future<String> doTaskTwoCallback() throws Exception {
        super.doTaskTwo();
        System.out.println("任务二,当前线程:" + Thread.currentThread().getName());
        return new AsyncResult<>("任务二完成");
    }

    @Async("taskExecutor")
    public Future<String> doTaskThreeCallback() throws Exception {
        super.doTaskThree();
        System.out.println("任务三,当前线程:" + Thread.currentThread().getName());
        return new AsyncResult<>("任务三完成");
    }
}

在 单元测试 用例中,注入 AsyncExecutorTask 对象,并在测试用例中执行 doTaskOne(),doTaskTwo(),doTaskThree() 三个方法。

@SpringBootTest
public class AsyncExecutorTaskTest {
    @Autowired
    private AsyncExecutorTask task;

    @Test
    public void testAsyncExecutorTask() throws Exception {
        task.doTaskOneCallback();
        task.doTaskTwoCallback();
        task.doTaskThreeCallback();

        sleep(30 * 1000L);
    }
}

执行一下上述的 单元测试,可以看到如下结果:

开始做任务一
开始做任务三
开始做任务二
完成任务二,耗时:3905毫秒
任务二,当前线程:taskExecutor-2
完成任务一,耗时:6184毫秒
任务一,当前线程:taskExecutor-1
完成任务三,耗时:9737毫秒
任务三,当前线程:taskExecutor-3

执行上面的单元测试,观察到 任务线程池 的 线程池名的前缀 被打印,说明 线程池 成功执行 异步任务!

三、优雅地关闭线程池

由于在应用关闭的时候异步任务还在执行,导致类似 数据库连接池 这样的对象一并被 销毁了,当 异步任务 中对 数据库 进行操作就会出错。

解决方案如下,重新设置线程池配置对象,新增线程池 setWaitForTasksToCompleteOnShutdown() 和 setAwaitTerminationSeconds() 配置:

@Bean("taskExecutor")
public Executor taskExecutor() {
    ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
    executor.setPoolSize(20);
    executor.setThreadNamePrefix("taskExecutor-");
    executor.setWaitForTasksToCompleteOnShutdown(true);
    executor.setAwaitTerminationSeconds(60);
    return executor;
}
  • setWaitForTasksToCompleteOnShutdown(true): 该方法用来设置 线程池关闭 的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean,这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
  • setAwaitTerminationSeconds(60): 该方法用来设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。

异步任务** 的 销毁 就会先于 数据库连接池对象 的销毁。

  • setAwaitTerminationSeconds(60): 该方法用来设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。

 到此这篇关于springboot为异步任务规划自定义线程池的实现的文章就介绍到这了,更多相关springboot异步自定义线程池内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Java/Android 相关文章推荐
Java如何实现树的同构?
Jun 22 Java/Android
详解Java实现数据结构之并查集
Jun 23 Java/Android
探讨Java中的深浅拷贝问题
Jun 26 Java/Android
springboot集成springCloud中gateway时启动报错的解决
Jul 16 Java/Android
java中为什么说子类的构造方法默认访问的是父类的无参构造方法
Apr 13 Java/Android
IDEA 2022 Translation 未知错误 翻译文档失败
Apr 24 Java/Android
Java版 单机五子棋
May 04 Java/Android
使用Postman测试需要授权的接口问题
Jun 21 Java/Android
SpringBoot使用ip2region获取地理位置信息的方法
Jun 21 Java/Android
Android实现图片九宫格
Jun 28 Java/Android
Spring Boot实现文件上传下载
Aug 14 Java/Android
Java Redisson多策略注解限流
Sep 23 Java/Android
详解Flutter自定义应用程序内键盘的实现方法
ConditionalOnProperty配置swagger不生效问题及解决
Jun 14 #Java/Android
Java异常体系非正常停止和分类
Android开发手册TextInputLayout样式使用示例
Jun 10 #Java/Android
Java实现简单小画板
Android开发EditText禁止输入监听及InputFilter字符过滤
Jun 10 #Java/Android
详解Spring Bean的配置方式与实例化
Jun 10 #Java/Android
You might like
PHP详细彻底学习Smarty
2008/03/27 PHP
PHP mb_convert_encoding文字编码的转换函数介绍
2011/11/10 PHP
PHP手机号码归属地查询代码(API接口/mysql)
2012/09/04 PHP
Yii2 RESTful中api的使用及开发实例详解
2016/07/06 PHP
TP(thinkPHP)框架多层控制器和多级控制器的使用示例
2018/06/13 PHP
datePicker——日期选择控件(with jquery)
2007/02/20 Javascript
编辑浪子版表单验证类
2007/05/12 Javascript
在一个js文件里远程调用jquery.js会在ie8下的一个奇怪问题
2010/11/28 Javascript
用函数模板,写一个简单高效的 JSON 查询器的方法介绍
2013/04/17 Javascript
高性能JavaScript循环语句和条件语句
2016/01/20 Javascript
AngularJS 如何在控制台进行错误调试
2016/06/07 Javascript
jQuery监听文件上传实现进度条效果的方法
2016/10/16 Javascript
AngularJS实现与Java Web服务器交互操作示例【附demo源码下载】
2016/11/02 Javascript
jquery实现搜索框功能实例详解
2018/07/23 jQuery
vue发送websocket请求和http post请求的实例代码
2019/07/11 Javascript
layui清空,重置表单数据的实例
2019/09/12 Javascript
layer.open组件获取弹出层页面变量、函数的实例
2019/09/25 Javascript
js找出5个数中最大的一个数和倒数第二大的数实现方法示例小结
2020/03/04 Javascript
vue实现打地鼠小游戏
2020/08/21 Javascript
[01:31:02]TNC vs VG 2019国际邀请赛淘汰赛 胜者组赛BO3 第一场
2019/08/22 DOTA
跟老齐学Python之编写类之三子类
2014/10/11 Python
face++与python实现人脸识别签到(考勤)功能
2019/08/28 Python
python3.7实现云之讯、聚合短信平台的短信发送功能
2019/09/26 Python
使用Python制作缩放自如的圣诞老人(圣诞树)
2019/12/25 Python
使用wxpy实现自动发送微信消息功能
2020/02/28 Python
通过代码实例了解Python异常本质
2020/09/16 Python
python如何实现DES加密
2020/09/21 Python
GetYourGuide台湾:预订旅游活动、景点和旅游项目
2019/06/10 全球购物
经贸日语毕业生自荐信
2013/11/03 职场文书
教学大赛获奖感言
2014/01/15 职场文书
贷款担保书范文
2014/05/13 职场文书
区级文明单位申报材料
2014/05/15 职场文书
推广普通话标语
2014/06/27 职场文书
护士爱岗敬业心得体会
2016/01/25 职场文书
《好妈妈胜过好老师》:每个孩子的优秀都是有源头的
2020/01/03 职场文书
Django利用AJAX技术实现博文实时搜索
2021/05/06 Python