【线程池】线程池的corePoolSize大小为什么设置为N+1或2N?

线程池的corePoolSize大小为什么设置为N+1或2N?

根据业务场景来设置线程池中的各个参数。

分析线程池处理的任务是CPU密集型还是IO密集型:

  • CPU密集型:corePoolSize = N + 1
  • IO密集型:corePoolSize = 2 * N

注意:N+1和2N都是经验值。

出发点:希望CPU一刻不停的工作。

为什么CPU密集型是N+1?

CPU密集型场景下,我们就不再考虑IO了。这时候CPU空闲的原因一般就是正在处理的任务出现错误,暂停了。这个时候如果我们有另一个任务恰好补上来,那CPU就没得歇了。所以是N+1。当然这也是经验值。

网上也有说法是,CPU密集型是N-2N。这个也不难理解,如果我们N个线程都在处理任务,他们发生了错误,每个都有个替补的补上,这就是2N了。

为什么IO密集型是2N?

理想线程数=最大并行数*(CPU计算时间+等待时间)/CPU计算时间

如果理想的认为CPU计算时间等于CPU等待时间,那么理想线程数就是2N了。

当然大部分场景下这是不可能的。一般Web项目中IO时间(CPU等待时间)都远高于CPU计算时间(因此MySQL才会用B+树做索引来减小每查找一层增加的IO)。比如一个简单的数据库查询,CPU计算时间可能只有0.1ms,CPU的IO时间则可能达到2ms,这样一算,就是21N了。所以说2N只是个理想的经验值,在高IO时间开销的场景,可以是10N、20N、30N等等。

在执行任务时动态调整线程池参数

线程池使用面临的核心问题在于:线程池的参数并不好配置。

如果无法找到合理的参数配置,那能否换一个角度考虑:降低修改参数配置的成本。

动态调整核心线程数和最大线程数

在执行任务时,线程池使用方调用setCorePoolSize方法设置corePoolSize之后,线程池会直接覆盖原来的corePoolSize值。

如果修改后的corePoolSize值小于当前工作线程数的话,说明有多余的工作线程,此时会向当前idle的工作线程发起中断请求以实现回收,多余的工作线程在下次idle的时候也会被回收。

如果修改后的corePoolSize值大于当前工作线程数且任务队列中有待执行任务,则线程池会创建新的工作线程来执行任务队列中的任务。

问题:如果把核心线程数调的过大,那么在业务低峰期的时候是否需要人工调低这个值呢?
回答:并不需要。corePoolSize中有一个参数叫做allowThreadTimeOut,它默认为flase,如果设置为true,就意味着当核心线程空闲时,keepAliveTime(空闲线程存活时间)后也会回收该核心线程。

动态调整阻塞队列(任务队列)的大小

问题:为什么没有提供修改任务队列大小的set方法呢?
回答:因为线程池参数中任务队列的capacity被final修饰了,无法修改。

可以把LinkedBlockingQueue的代码复制一份出来,修改个名字,然后把capacity参数的final修饰符去掉,并提供其对应的get/set方法。然后在程序里面把原来的队列换掉即可。

注意:线程池被创建后如果没有任务过来,里面是不会有线程的。如果需要对线程池进行预热的话,可以调用下面的两个方法,一是启动全部线程(prestartAllCoreThreads),二是启动一个线程(prestartCoreThread)。

注意:核心线程默认不会被回收,如果需要回收,需要调用allowCoreThreadTimeOut方法。

<think>好的,用户现在想了解线程池的工作流程以及相关参数配置。首先,我需要回顾用户提供的引用内容,确保回答准确。用户之前的问题是关于数据库索引失效的原因,现在转向线程池,可能是在学习Java并发编程。 根据引用,用户提供了多个关于线程池参数的资料,特别是ThreadPoolExecutor的构造参数、工作流程、常见线程池类型等。用户需要的是工作流程和参数详解,所以需要将这两部分结合,结构清晰。 首先,线程池的工作流程需要分步骤说明,比如提交任务时,核心线程是否已满,队列是否已满,最大线程数是否已满,以及拒绝策略。这可以结合引用3中的底层工作原理来展开。 参数部分有七个核心参数,需要逐一解释每个参数的作用和配置建议。引用3详细列出了七个参数,引用2和4也提到了参数和线程池类型。要注意参数如corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。同时,引用5提到了自定义配置,可能可以加入实际配置的例子。 用户可能想知道如何选择合适的参数,比如如何决定核心线程数,队列类型的选择,拒绝策略的应用场景。需要结合引用中的例子,比如Executors工具类创建的几种线程池,如newFixedThreadPool、newCachedThreadPool等,来说明不同参数配置的效果。 另外,用户可能对工作流程中的每个判断条件不太清楚,需要用流程图步骤说明来帮助理解。例如,当任务提交时,是否先创建核心线程,然后进队列,最后扩展至最大线程数,最后拒绝。这部分引用3中的工作原理可以详细说明。 需要注意回答的结构是否清晰,分点列出工作流程和参数详解,可能使用标题分隔。同时,结合用户之前的索引失效问题,可能没有直接关联,但需要确保当前回答专注在线程池上。 用户可能还有更深层的需求,比如如何调优线程池,避免常见错误,者实际项目中如何配置,这些可以在相关问题中提出,引导用户进一步学习。例如,如何监控线程池状态,如何选择合适的拒绝策略,线程池的优缺点等。 最后,确保回答使用中文,LaTeX格式正确,特别是参数名可能需要用$符号包裹,如$corePoolSize$。同时检查引用标记是否正确,引用[^1]到[^5]对应之前提供的资料,但用户没有给出具体引用来源,所以可能需要省略引用标记,者假设引用编号对应之前的说明。不过用户示例中的回答使用了引用标记,但当前问题中的引用是用户提供的站内引用,可能需要按用户指示处理。根据用户指令,引用标识应自然添加,但用户提供的引用可能没有具体来源,所以可能需要在回答末尾统一注明引用来源,根据情况处理。 现在需要组织这些信息,确保逻辑清晰,分步骤解释工作流程,详细说明每个参数,并给出配置建议和示例。同时生成相关问题,帮助用户进一步学习。</think>### 线程池工作流程及核心参数详解 --- #### **一、线程池工作流程** 线程池通过复用线程资源高效管理多线程任务,其工作流程可分为以下步骤(以`ThreadPoolExecutor`为例): 1. **任务提交** 用户调用`execute()``submit()`方法提交任务。 2. **核心线程分配** - 若当前运行线程数 < $corePoolSize$,立即创建新线程执行任务[^3]。 - 若核心线程已满,任务进入阻塞队列($workQueue$)等待。 3. **队列检查** - 若队列未满,任务加入队列等待空闲线程处理。 - 若队列已满,尝试创建新线程(总数不超过$maximumPoolSize$)执行任务[^3]。 4. **最大线程扩展** - 若队列已满且当前线程数 < $maximumPoolSize$,创建新线程处理任务。 - 若队列已满且线程数已达最大值,触发拒绝策略($handler$)[^3]。 5. **空闲线程回收** 当线程空闲时间超过$keepAliveTime$(且线程数 > $corePoolSize$),多余线程被销毁[^3]。 --- #### **二、线程池核心参数详解** 线程池通过7个关键参数控制行为,以下是详细说明: | **参数** | **作用** | **配置建议** | |--------------------|------------------------------------------------------------------------|---------------------------------------------------------------------------| | **$corePoolSize$** | 常驻核心线程数,即使空闲也不会被销毁 | 根据CPU密集型IO密集型任务调整:<br> - CPU密集型:$N_{cpu}+1$<br> - IO密集型:$2N_{cpu}$[^2] | | **$maximumPoolSize$** | 线程池最大线程数(含核心线程) | 需结合队列容量设置,避免过大导致资源耗尽 | | **$keepAliveTime$** | 非核心线程的空闲存活时间 | 默认单位秒,建议根据任务波动频率调整(如10-60秒) | | **$unit$** | $keepAliveTime$的时间单位(秒、毫秒等) | 通常使用$TimeUnit.SECONDS$ | | **$workQueue$** | 任务队列,用于缓存待执行任务 | 常见类型:<br> - `LinkedBlockingQueue`(无界)<br> - `ArrayBlockingQueue`(有界)<br> - `SynchronousQueue`(直接传递)[^3] | | **$threadFactory$**| 线程工厂,用于自定义线程创建(如命名、优先级) | 建议自定义线程名称,便于日志排查问题 | | **$handler$** | 拒绝策略,当队列和线程池均满时触发 | 四种内置策略:<br> - `AbortPolicy`(默认,抛异常)<br> - `CallerRunsPolicy`(调用者线程执行)<br> - `DiscardOldestPolicy`(丢弃队列最旧任务)<br> - `DiscardPolicy`(静默丢弃) | --- #### **三、配置示例与常见线程池** 1. **手动创建线程池** ```java ThreadPoolExecutor executor = new ThreadPoolExecutor( 5, // corePoolSize 10, // maximumPoolSize 60, TimeUnit.SECONDS, // keepAliveTime new ArrayBlockingQueue<>(100), // workQueue Executors.defaultThreadFactory(), // threadFactory new ThreadPoolExecutor.AbortPolicy() // handler ); ``` 2. **Executors工具类预定义线程池** - **newFixedThreadPool**:固定大小线程池(核心线程=最大线程,无界队列)[^4] ```java ExecutorService fixedPool = Executors.newFixedThreadPool(5); ``` - **newCachedThreadPool**:弹性线程池(核心线程=0,最大线程=Integer.MAX_VALUE,队列为`SynchronousQueue`)[^4] - **newSingleThreadExecutor**:单线程池(保证任务顺序执行)[^4] --- #### **四、参数调优注意事项** 1. **避免无界队列**:可能导致内存溢出(如`LinkedBlockingQueue`未指定容量)。 2. **合理设置最大线程数**:过高会引发频繁上下文切换,过低导致任务堆积。 3. **拒绝策略选择**:根据业务容忍度选择,如支付系统推荐`CallerRunsPolicy`保证任务不丢失。 4. **监控线程池状态**:通过`getActiveCount()`、`getQueue().size()`等接口实时观察负载。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

寂寞烟火~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值