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

网友投稿 543 2022-07-23


目录一、Spring Boot任务线程池二、自定义线程池三、优雅地关闭线程池

一、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;

}

}

上面我们通过使用 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 doTaskOneCallback() throws Exception {

super.doTaskOne();

System.out.println("任务一,当前线程:" + Thread.currentThread().getName());

return new AsyncResult<>("任务一完成");

}

@Async("taskExecutor")

public Future doTaskTwoCallback() tIxKXrJQKhrows Exception {

super.doTaskTwo();

System.out.println("任务二,当前线程:" + Thread.currentThread().getName());

return new AsyncResult<>("任务二完成");

}

@Async("taskExecutor")

public Future doTaskThreeCallback() throws Exception {

super.doTaskThree();

System.out.println("任务三,当前线程:" + Thread.currentThhttp://read().getName());

return new AsyncResult<>("任务三完成");

}

}

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

@SpringBootTest

public class AsyncExecutorTaskTest {

@AutoIxKXrJQKwired

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


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:ConditionalOnProperty配置swagger不生效问题及解决
下一篇:Java异常体系非正常停止和分类
相关文章

 发表评论

暂时没有评论,来抢沙发吧~