java 线程池扩容_106. 生产环境中的线程池自动扩容与缩容的动态资源分配经验...

本文探讨了在生产环境中如何实现线程池的自动扩容与缩容,通过Hystrix实现动态资源分配,并解决了配置withMaximumSize偶尔失效的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

# 106. 生产环境中的线程池自动扩容与缩容的动态资源分配经验

可能会出现一种情况,比如说我们的某个依赖在高峰期,需要耗费 100 个线程,但是在那个时间段,刚好其他的依赖的线程池其实就维持一两个就可以了

但是,如果我们都是设置死的,每个服务就给 10个 线程,那就很坑,可能就导致有的服务在高峰期需要更多的资源,但是没资源了,导致很多的 reject

但是其他的服务,每秒钟就易一两个请求,结果也占用了 10个 线程,占着茅坑不拉屎

可以利用 hystrix 的配置成弹性的线程资源调度的模式

通过以下测试方法

输出日志

三个被降级,因为一次只能最大 10 个线程,会发现所有的线程名称都是新的,

所以这里每一个 command 都是一个新的线程,但是在官网文档中又看到说,在空闲之后,

会把线程销毁。这个就看不太明白是怎么一回事情了

# 配置 withMaximumSize 无效解决

项目中实战 hystrix 的时候发现这里的配置老是不太对,

如下面这个配置,我给了最大线程 100,但是给 50 个并发线程就会大量的降级

这个问题困扰了我很长时间,后来跟踪源码,当 command 被拒绝的时候,会报错

进入源码后会发现,这里会先判定线程池队列大小是否已经超过了 queueSizeRejectionThreshold 大小;

在前面已经学到过该大小是可以动态调整的。含义是调整队列大小;

那么这里有一个疑问了,为什么会先判断是否已经超了?

会发现使用了 ThreadPoolExecutor.submit 方法;我在 debug 的时候看过了 ThreadPoolExecutor 的参数,的确是我们设置的;

之前不知道 ThreadPoolExecutor 的用法,所以根本想不到为什么。

百度之后,ThreadPoolExecutor 会检查队列,但是在这之前 hystrix 会先检查一次,所以就导致了还没有进入线程池就报错了;

那么问题是为什么队列里面有这么的线程,而且在日志中看不出来到底有几个线程在执行呢?最简单的回答就是 hystrix 包装了线程池;

最后找到一个根源问题:百度之后,创建 ThreadPoolExecutor 的时候,会传入一个 BlockingQueue,如果使用无限容量的阻塞队列(如 LinkedBlockingQueue)时,

不会创建临时线程(因为队列不会满),所以线程数保持 corePoolSize。 而刚好在 debug 时看到的队列就是 LinkedBlockingQueue;

这个时候就真相大白了;原因就是设置了队列大小!下面使用一个 ThreadPoolExecutor 的测试用例来浮现这个问题 当使用 LinkedBlockingDeque 时,运行后观察控制台,只会发现最多只有 20 个线程被复用;这里的队列大小是 20;

当使用 SynchronousQueue 时,就会瞬间出现 40 个线程

回到最初的问题:配置 withMaximumSize 偶尔有效偶尔无效

解决方案:想使用 withMaximumSize 动态调整线程数量的时候,就不要设置等待队列; 如下

TIP

前面和本章讲解的配置相关实践的内容,这些在 官网文档(opens new window) 中都有写到,老样子就是全部是英文文档

<think>我们正在讨论动态线程池在调整参数时如何处理正在执行的线程。根据用户上一次的问题,他特别关注“当有线程正在执行时如何处理动态配置调整”。结合提供的引用,我们需要解释动态线程池如何在不影响正在执行任务的前提下调整参数。引用[1]中给出了一个使用示例,其中包含自适应调整器(AdaptiveThreadPoolAdjuster),它定期调整线程池的核心线程数。引用[2]则提到了线程池的基本结构,其中任务管理(生产者)和线程管理(消费者)的交互。关键点:动态调整核心线程数和最大线程数时,不能中断正在执行的任务,只能影响空闲线程或未来的任务分配。因此,我们可以从以下角度回答:1.核心线程数的调整(减少)只影响空闲线程:当减少核心线程数时,多出的线程不会立即被回收,而是在空闲时且超过keepAliveTime后才会被终止。这样,正在执行任务的线程不会受到影响。2.最大线程数的调整:主要影响后续任务的执行策略(例如,当调整最大线程数时,后续任务是否能创建新线程),对正在执行的任务无影响。3.使用自适应调整器时,调整是周期性的,且调整过程中不会中断正在执行的任务。另外,我们可以参考JavaThreadPoolExecutor的setCorePoolSize和setMaximumPoolSize方法的实现原理:它们只是修改了内部变量,不会中断正在运行的worker线程。当调整核心线程数时,如果当前有超过新核心线程数数量的线程,那么这些多余的线程将在下一次空闲超时时被终止(前提是allowCoreThreadTimeOut被设置或者该线程是大于核心线程的线程)。根据上述原理,我们组织回答:</think> ### 动态线程池中处理正在执行线程的配置调整机制 在动态线程池中,当有线程正在执行任务时进行参数调整需要特殊处理,主要采用以下核心机制: #### 1. **异步无损调整机制** 动态调整不会中断正在执行的任务,而是: - 对核心线程数的减少:新配置仅影响**后续创建**的线程[^1] - 对最大线程数的调整:已存在的执行线程会**继续完成当前任务** - 调整后立即生效的标志位:`volatile int corePoolSize`等关键参数,确保新提交任务遵循新规则 ```java // ThreadPoolExecutor中的设置方法 public void setCorePoolSize(int corePoolSize) { if (corePoolSize < 0) throw new IllegalArgumentException(); int delta = corePoolSize - this.corePoolSize; this.corePoolSize = corePoolSize; // 仅当增加核心线程数时立即创建新线程 if (workerCountOf(ctl.get()) > corePoolSize) interruptIdleWorkers(); // 仅中断空闲线程 } ``` #### 2. **线程生命周期管理 当减少线程数时: 1. 不会强制终止正在执行任务的线程 2. 空闲线程在等待`keepAliveTime`后自动终止 3. 新增`interruptIdleWorkers()`仅影响空闲线程: ```java private void interruptIdleWorkers() { for (Worker w : workers) { Thread t = w.thread; // 仅中断未持有锁的线程(即空闲线程) if (!t.isInterrupted() && w.tryLock()) { try { t.interrupt(); // 安全中断 } finally { w.unlock(); } } } } ``` #### 3. **队列平滑迁移机制** 当调整工作队列量时: 1. 已有任务继续执行不受影响 2. 新提交任务: - 新队列使用新量配置 - 旧队列中任务转移完成后才销毁 3. 使用`ReentrantLock`保证转移操作的原子性 #### 4. **自适应调节器的实现 如引用[1]所示的自适应调整器: ```java adjuster.startAdjustment(5, TimeUnit.SECONDS); // 每5秒检测一次 ``` 其工作原理: 1. 监测周期内不中断正在执行任务 2. 仅在新任务提交时应用配置变更 3. 基于历史负载预测决定是否需要扩容/ ### 关键注意事项 1. **避免饥饿问题**:小队列量可能导致新任务被拒绝,需搭配合理拒绝策略 2. **资源竞争控制**:配置变更时需加锁保证线程安全 3. **监控兜底机制**:实时监控活跃线程数,避免因配置错误导致线程泄漏 > 实际应用中,美团动态线程池通过`Worker`状态机管理线程生命周期,确保调整时:<br> > - 运行中任务线程状态为`RUNNING`的不受影响<br> > - 阻塞状态线程会被唤醒并检查新配置<br> > - 新增任务直接应用最新参数[^1][^2] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值