springboot为异步任务规划自定义线程池如何实现
导读:本文共3846.5字符,通常情况下阅读需要13分钟。同时您也可以点击右侧朗读,来听本文内容。按键盘←(左) →(右) 方向键可以翻页。
摘要: 一、Spring Boot任务线程池线程池的作用防止资源占用无限的扩张调用过程省去资源的创建和销毁所占用的时间在高并发环境下,不断的分配新资源,可能导致系统资源耗尽。所以为了避免这个问题,我们为异步任务规划一个线程池。当然,如果没有配置线程池的话,springboot会自动配置一个ThreadPoolTaskExecutor 线程池到bean当中。#核心线程数... ...
目录
(为您整理了一些要点),点击可以直达。一、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=falsespring.task.execution.shutdown.await-termination-period=#线程名称前缀spring.task.execution.thread-name-prefix=task-
在springboot配置文件中加入上面的配置,即可实现ThreadPoolTaskExecutor 线程池。
二、自定义线程池
有的时候,我们希望将系统内的一类任务放到一个线程池,另一类任务放到另外一个线程池,所以使用Spring Boot自带的任务线程池就捉襟见肘了。下面介绍自定义线程池的方法。
创建一个 线程池配置类 TaskConfiguration ,并配置一个 任务线程池对象 taskExecutor。
@ConfigurationpublicclassTaskConfiguration{@Bean("taskExecutor")publicExecutortaskExecutor(){ThreadPoolTaskExecutorexecutor=newThreadPoolTaskExecutor();executor.setCorePoolSize(10);executor.setMaxPoolSize(20);executor.setQueueCapacity(200);executor.setKeepAliveSeconds(60);executor.setThreadNamePrefix("taskExecutor-");executor.setRejectedExecutionHandler(newCallerRunsPolicy());returnexecutor;}}
上面我们通过使用 ThreadPoolTaskExecutor 创建了一个 线程池,同时设置了以下这些参数:
Reject策略预定义有四种:
AbortPolicy,用于被拒绝任务的处理程序,它将抛出RejectedExecutionException。
CallerRunsPolicy,用于被拒绝任务的处理程序,它直接在execute方法的调用线程中运行被拒绝的任务。
DiscardOldestPolicy,用于被拒绝任务的处理程序,它放弃最旧的未处理请求,然后重试execute。
DiscardPolicy,用于被拒绝任务的处理程序,默认情况下它将丢弃被拒绝的任务。
创建 AsyncExecutorTask类,三个任务的配置和 AsyncTask 一样,不同的是 @Async 注解需要指定前面配置的 线程池的名称 taskExecutor。
@ComponentpublicclassAsyncExecutorTaskextendsAbstractTask{@Async("taskExecutor")publicFuture<String>doTaskOneCallback()throwsException{super.doTaskOne();System.out.println("任务一,当前线程:"+Thread.currentThread().getName());returnnewAsyncResult<>("任务一完成");}@Async("taskExecutor")publicFuture<String>doTaskTwoCallback()throwsException{super.doTaskTwo();System.out.println("任务二,当前线程:"+Thread.currentThread().getName());returnnewAsyncResult<>("任务二完成");}@Async("taskExecutor")publicFuture<String>doTaskThreeCallback()throwsException{super.doTaskThree();System.out.println("任务三,当前线程:"+Thread.currentThread().getName());returnnewAsyncResult<>("任务三完成");}}
在 单元测试 用例中,注入 AsyncExecutorTask 对象,并在测试用例中执行 doTaskOne(),doTaskTwo(),doTaskThree() 三个方法。
@SpringBootTestpublicclassAsyncExecutorTaskTest{@AutowiredprivateAsyncExecutorTasktask;@TestpublicvoidtestAsyncExecutorTask()throwsException{task.doTaskOneCallback();task.doTaskTwoCallback();task.doTaskThreeCallback();sleep(30*1000L);}}
执行一下上述的 单元测试,可以看到如下结果:
开始做任务一
开始做任务三
开始做任务二
完成任务二,耗时:3905毫秒
任务二,当前线程:taskExecutor-2
完成任务一,耗时:6184毫秒
任务一,当前线程:taskExecutor-1
完成任务三,耗时:9737毫秒
任务三,当前线程:taskExecutor-3
执行上面的单元测试,观察到 任务线程池 的 线程池名的前缀 被打印,说明 线程池 成功执行 异步任务!
三、优雅地关闭线程池
由于在应用关闭的时候异步任务还在执行,导致类似 数据库连接池 这样的对象一并被 销毁了,当 异步任务 中对 数据库 进行操作就会出错。
解决方案如下,重新设置线程池配置对象,新增线程池 setWaitForTasksToCompleteOnShutdown() 和 setAwaitTerminationSeconds() 配置:
@Bean("taskExecutor")publicExecutortaskExecutor(){ThreadPoolTaskSchedulerexecutor=newThreadPoolTaskScheduler();executor.setPoolSize(20);executor.setThreadNamePrefix("taskExecutor-");executor.setWaitForTasksToCompleteOnShutdown(true);executor.setAwaitTerminationSeconds(60);returnexecutor;}
setWaitForTasksToCompleteOnShutdown(true): 该方法用来设置 线程池关闭 的时候 等待 所有任务都完成后,再继续 销毁 其他的 Bean,这样这些 异步任务 的 销毁 就会先于 数据库连接池对象 的销毁。
setAwaitTerminationSeconds(60): 该方法用来设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
异步任务** 的 销毁 就会先于 数据库连接池对象 的销毁。
setAwaitTerminationSeconds(60): 该方法用来设置线程池中 任务的等待时间,如果超过这个时间还没有销毁就 强制销毁,以确保应用最后能够被关闭,而不是阻塞住。
</div> <div class="zixun-tj-product adv-bottom"></div> </div> </div> <div class="prve-next-news">
springboot为异步任务规划自定义线程池如何实现的详细内容,希望对您有所帮助,信息来源于网络。