SpringBoot线程池使用技巧

您所在的位置:网站首页 白昼之夜纯音乐免费下载百度云资源网盘 SpringBoot线程池使用技巧

SpringBoot线程池使用技巧

2023-05-11 23:32| 来源: 网络整理| 查看: 265

一、避免触发拒绝策略 1、背景

当SpringBoot线程池阻塞队列无剩余容量且活跃线程数量达到最大线程数时,会触发拒绝策略。拒绝策略有四种:AbortPloicy(抛出异常)、CallerRunsPolicy(由调用方执行)、DiscardPolicy(丢弃超出的任务)、DiscardOldestPolicy(丢弃阻塞队列中最早的任务)。 在一些场景下我们为了所有异步任务都能正常执行且保持主线程不被占用,不想触发任何一种拒绝策略,可以在调用异步任务前,判断阻塞队列是否存在剩余容量。

2、解决思路

通过ThreadPoolTaskExecutor中的initializeExecutor方法可以看到,ThreadPoolTaskExecutor对象在初始化时,实际上是创建一个ThreadPoolExecutor对象并作为ThreadPoolTaskExecutor对象的一个属性,所以我们可以通过ThreadPoolTaskExecutor对象的getThreadPoolExecutor方法获取原始的线程池对象(ThreadPoolExecutor对象)。

public ThreadPoolExecutor getThreadPoolExecutor() throws IllegalStateException { Assert.state(this.threadPoolExecutor != null, "ThreadPoolTaskExecutor not initialized"); return this.threadPoolExecutor; } protected ExecutorService initializeExecutor(ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { BlockingQueue queue = this.createQueue(this.queueCapacity); ThreadPoolExecutor executor; if (this.taskDecorator != null) { executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler) { public void execute(Runnable command) { Runnable decorated = ThreadPoolTaskExecutor.this.taskDecorator.decorate(command); if (decorated != command) { ThreadPoolTaskExecutor.this.decoratedTaskMap.put(decorated, command); } super.execute(decorated); } }; } else { executor = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, (long)this.keepAliveSeconds, TimeUnit.SECONDS, queue, threadFactory, rejectedExecutionHandler); } if (this.allowCoreThreadTimeOut) { executor.allowCoreThreadTimeOut(true); } this.threadPoolExecutor = executor; return executor; }

阻塞队列(BlockingQueue)是ThreadPoolExecutor对象中的一个属性,可以通过getQueue()方法获取。

public BlockingQueue getQueue() { return workQueue; }

BlockingQueue中有一个remainingCapacity()方法可以返回队列中的剩余容量,当返回值为0时说明阻塞队列无剩余容量。

3、实现代码

将线程池的核心线程数量设置为1,缓冲队列容量设置为4,拒绝策略为AbortPloicy(抛出异常)。

@Configuration public class ThreadPoolConfig { @Primary @Bean(name = "threadPool01") public ThreadPoolTaskExecutor threadPool01() { // 创建线程池任务执行器对象 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 设置核心线程数量 executor.setCorePoolSize(1); // 设置最大线程数量 executor.setMaxPoolSize(1); // 设置阻塞队列容量 executor.setQueueCapacity(4); // 设置线程空闲时间,默认为 60 秒 executor.setKeepAliveSeconds(60); // 设置是否支持回收核心线程,默认为 false executor.setAllowCoreThreadTimeOut(false); // 设置线程名称前缀,若不设置则根据对象的 beanName 生成 executor.setThreadNamePrefix("threadPool01-"); // 设置线程池拒绝策略,默认为 AbortPolicy,即线程数量达到最大线程数量,且阻塞队列容量已满,再添加任务则抛出异常。 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy()); // 初始化 executor.initialize(); return executor; } }

循环执行异步任务,并在调用异步任务前判断缓冲队列是否存在剩余容量,无剩余容量则一直等待至有容量。

/** * 避免拒绝策略 */ @Test public void avoidRejectedExecutionHandler() throws InterruptedException { for (int i = 0; i < 10; i++) { // 等待至缓冲队列存在容量再执行异步任务 while (threadPoolTaskExecutor.getThreadPoolExecutor().getQueue().remainingCapacity() == 0) { log.info("线程池缓冲队列无剩余容量,等待……"); TimeUnit.SECONDS.sleep(1); } asyncService.simpleAsync(); } // 等待一段时间让异步方法执行结束 TimeUnit.SECONDS.sleep(10); }

执行结果不会抛出异常,而是在队列无容量是等待至有容量后再执行异步任务。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3