Java线程池参数动态化实践

为什么需要线程池参数动态化?

日常业务开发中,线程池参数是很难计算准确的,往往需要在实践中不断的调整才能得到一个较为合理的取值。在取值不是那么明确之前,每次修改参数都需要重新部署服务才能生效,这显然不是一个优雅的实践方案,所以我们需要将线程池参数动态化,一旦线程池发生故障,可以快速调整参数且能立即生效,有效缩短故障时间。

代码实践

代码基于 Springboot 2.7.0

  1. 先配置一个ThreadPoolTaskExecutor实例,初始核心线程数为1,最大线程数为2,队列容量为5
    @Bean("myExecutor")
    public ThreadPoolTaskExecutor asyncExecutorService() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(2);
        executor.setQueueCapacity(5);
        executor.setKeepAliveSeconds(10);
        executor.setThreadNamePrefix("My-Executor-");
        executor.initialize();
        return executor;
    }
  1. 创建两个api,/business 用于模拟业务请求,/thread-pool/param 用于动态修改线程池参数。
@Slf4j
@RestController
public class MyController {
    
    @Autowired
    private ThreadPoolTaskExecutor myExecutor;

    @PostMapping("/business")
    public void newBusiness() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            final int index = i + 1;
            try {
                myExecutor.execute(() -> {
                    try {
                        Thread.sleep(3_1000);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    log.info("This is my business{}", index);
                });
            }catch (RejectedExecutionException e){
                log.error("Business{} was rejected", index);
            }

        }

    }

    @PutMapping("/thread-pool/param")
    public void updateThreadPool(@RequestBody UpdateThreadPoolParam updateData) {

        log.info("corePoolSize before => {}", myExecutor.getCorePoolSize());
        log.info("maxPoolSize before => {}", myExecutor.getMaxPoolSize());
        log.info("keepAliveSeconds before => {}", myExecutor.getKeepAliveSeconds());


        myExecutor.setCorePoolSize(updateData.getCorePoolSize());
        myExecutor.setMaxPoolSize(updateData.getMaxPoolSize());
        myExecutor.setKeepAliveSeconds(updateData.getKeepAliveSeconds());


        log.info("corePoolSize after => {}", myExecutor.getCorePoolSize());
        log.info("maxPoolSize after => {}", myExecutor.getMaxPoolSize());
        log.info("keepAliveSeconds after => {}", myExecutor.getKeepAliveSeconds());

    }


}
  1. 执行请求(curl -X POST /business)

我们发起了10个任务,因为最大线程数是2,队里容量为5,初始线程池最多能同时持有7个任务,2个执行中的任务和5个队列中等待的任务,所以最终结果就是最后3个任务被拒绝。
Java线程池参数动态化实践

  1. 执行请求 (curl -H “Content-Type:application/json” -X PUT -d ‘{“corePoolSize”:3, “maxPoolSize”:10, “keepAliveSeconds”:30}’ /thread-pool/param) 修改参数
  • 核心线程数 = 3
  • 最大线程数 = 10
  • 线程空闲等待时间 = 30s

可以看到,线程池参数已经修改成功。
Java线程池参数动态化实践
5. 再次执行请求(curl -X POST /business) 验证修改后的参数

同样是发起10个任务,由运行结果可知,10个任务共由5个线程一起处理。因为队列容量为5,所以只需要5个线程(3个核心线程 + 2个非核心线程)即可。
所以目前线程池参数已经不是最初的值了(核心线程为1,最大线程为2),验证通过。
在这里插入图片描述

注:构造线程池时有8个参数,但是最核心的是3个:核心线程数、最大线程数,工作队列

本实践中只实现了前两个参数的动态修改,因为JDK自带的ThreadPoolExecutor并没有对外提供设置workQueue及其容量大小的方法。虽然spring提供的ThreadPoolTaskExecutor可以设置队列容量,但其只在初始化之前生效,一旦初始化完成再去修改这个值就是无效的。如 ThreadPoolTaskExecutor源码所示:

    // 设置核心线程数时会同步更新threadPoolExecutor中的核心线程数
	public void setCorePoolSize(int corePoolSize) {
synchronized (this.poolSizeMonitor) {
        if (this.threadPoolExecutor != null) {
            this.threadPoolExecutor.setCorePoolSize(corePoolSize);
        }
        this.corePoolSize = corePoolSize;
        }
    }

    // 而设置队列容量的时候只更新其本身的属性值
	public void setQueueCapacity(int queueCapacity) {
		this.queueCapacity = queueCapacity;
	}

如果确实要实现动态调整工作队列,我们可以通过ThreadPoolExecutor提供的getQueue()方法获取到工作队列对象实例然后修改其容量,但是默认BlockingQueue一旦创建就没法修改其容量大小,所以我们需要自己实现一个可以动态调整容量大小的队列即可。
Java线程池参数动态化实践

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

i余数

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值