线程池种类以及参数设置问题

本文介绍了JDK1.5中引入的concurrent包中的线程池实现ThreadPoolExecutor,包括五种不同类型的线程池创建配置及其应用场景。此外,还详细讨论了线程池的关键参数设置,如核心线程数、最大线程数等,以及如何根据系统负载调整这些参数。

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

JDK1.5中引入了强大的concurrent包,线程池的实现ThreadPoolExcutor。

线程池种类

    通常开发者都是利用Executors提供的通用线程池创建方法,去创建不同配置的线程池,主要区别在于ExecutorService类型或者不同的初始参数。

    Executors目前提供了五种不同的线程池创建配置:

  • newCachedThreadPool(),它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过60秒,则被终止并移出缓存;长时间闲置时,这种线程池并不会消耗什么资源,其内部使用SynchronousQueue作为工作队列;
  • newFixedThreadPool(),创建固定大小的线程池,背后使用的是无界的工作队列,任何时候最多有nThreads个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目nThreads。
  • newSingleThreadExcuteor(),它的特点在于工作线程数目被限制为1,操作一个无界的工作队列,所以保证了所有任务都是被顺序执行的,最多只会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。
  • newSingleThreadScheduledExcutor()和newScheduleThreadPool(int corePoolSize),创建的是个ScheduledExcutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
  • newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。

阻塞队列(runnableTaskQueue)

  • ArrayBlockingQueue:一个基于数组结构的有界阻塞队列,此队列按照先进先出原则对元素进行排序;
  • LinkedBlockingQueue:一个基于链表结构的阻塞队列,按照先进先出对元素进行排序。吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Excutors.newFixedThreadPool()使用了这个队列;
  • SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。吞吐量要高于LinkedBlockingQueue。静态工厂方法Excutors.newCachedThreadPool()使用了这个队列;
  • PriorityBlockingQueue:一个具有优先级的无界阻塞队列。

系统负载

    参数的设置跟系统的负载有直接的关系,系统负载相关参数:

  • tasks,每秒需要处理的最大任务数
  • tasktime,处理每个任务所需要的时间
  • responsetime,系统允许任务最大的响应时间,比如每个任务的响应时间不得超过2秒

ThreadPoolExcutor类可设置的参数有:

1.corePoolSize:

    核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理;

    核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。

    每个任务需要tasktime秒处理,则每个线程每秒可以处理1/tasktime个任务。系统每秒有tasks个任务,所以需要的线程数为tasks/(1/tasktime),即tasks*tasktime个线程数。

    假设系统每秒任务数为100-1000,每个任务耗时0.1秒,那么需要10-100个线程, 核心线程数应设置为大于10,具体数字最好根据8020原则,即80%情况下系统每秒任务数,若系统80%情况下每秒任务数小于200,最多时为1000,那么可以设置为20。

2.maxPoolSize:

    当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数等于最大线程数,则已经超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。

    当系统负载达到最大值的时候,核心线程数已经无法按时处理完所有任务,这时候就需要增加线程。每秒200个任务需要20个线程,当每秒达到1000个任务的时候,需要(1000-queueCapacity)*(20/200)=60个线程,可以将最大线程数设置为60。

3.keepAliveTime:

    当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量达到corePoolSize。如果allowCoreThreadTimeout被设置为true,则所有线程均会退出直到线程数量为0.

4.allowCoreThreadTimeout:

    是否允许核心线程空闲退出。

5.queueCapacity:

    任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。

    任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为:

(corePoolSize/tasktime)*responsetime:20/0.1*2=400,即队列长度可以设置为400。

    队列长度设置过大,会导致任务响应时间过长,切忌以下写法:

LinkedBlockingQueue queue=new LinkedBlockingQueue();

    这实际上是将队列长度设置为Integer.MAX_VALUE,将会导致线程数量永远为corePoolSize,再也不会增加,当任务数量陡增时,任务响应时间也随之陡增。

6.RejectedExcutionHandler:饱和策略

当队列和线程池都满了,说明线程池处于饱和状态,那么必须对新提交的任务采用一种特殊的策略来进行处理。这个策略默认配置是AbortPolicy,表示无法处理新的任务而抛出异常。JAVA提供了4中策略:

  • AbortPolicy:直接抛出异常
  • CallerRunsPolicy:只用调用所在的线程运行任务
  • DiscardOledestPolicy:丢弃队列里最近的一个任务,并执行当前任务
  • DiscardPolicy:不处理,丢弃掉

    以上关于线程数量的计算并没有考虑CPU的情况。若结合CPU的情况,比如,当线程数量达到50时,CPU达到100%,则将maxPoolSize设置为60也不合适,此时若系统负载长时间维持在每秒1000个任务,则超出线程池处理能力,应设法降低每个任务的处理时间(tasktime)。

参考:https://blog.youkuaiyun.com/zhouhl_cn/article/details/7392607

<think>嗯,用户的问题是关于线程池队列大小是否可以手动设置以及如何配置线程池参数。首先,我需要确认线程池的各个参数是否允许用户自定义。根据之前学过的知识,线程池通常由核心线程数、最大线程数、队列容量、拒绝策略等组成。比如Java中的ThreadPoolExecutor构造函数就有这些参数,所以队列大小应该可以设置的。 接下来,用户可能想知道具体怎么设置队列的大小。不同的队列类型可能有不同的配置方式,比如LinkedBlockingQueue可以在初始化时指定容量。需要说明如何创建这样的队列,并给出示例代码,这样用户会更清楚。 然后,配置线程池参数需要考虑哪些因素呢?用户可能不了解如何根据业务场景来调整参数。这里需要结合系统引用中的信息,比如引用[3]提到的计算密集型和I/O密集型任务的区别。计算密集型任务线程数不宜过多,而I/O密集型可以适当增加线程数。队列大小的设置还要考虑系统内存,避免内存溢出,比如引用[5]提到的队列内存大小。 另外,用户可能对如何平衡线程数和队列容量有疑问。比如在高并发场景下,是先开更多线程还是让任务排队?这需要解释线程池的工作流程,当任务数超过核心线程数时,会先进入队列,队列满了才会创建新线程直到达到最大线程数。所以队列太大可能导致响应延迟,太小则可能频繁触发拒绝策略。 还需要提到拒绝策略的选择,不同的策略对系统的影响不同,比如直接丢弃还是抛出异常。用户可能不知道有哪些选项,需要列举常见的策略并建议适用场景。 最后,用户可能想了解具体的配置步骤或工具,比如动态调整参数或使用监控工具。引用[5]提到的PoolSizeCalculator类可能是一个例子,说明如何根据负载计算合适的参数,但需要确认这个类是否是某个框架的一部分,或者是否需要自行实现。 总结一下,回答需要覆盖以下几点:队列大小可以手动设置,如何设置(代码示例),配置参数时的考虑因素(任务类型、系统资源、业务需求),线程池的工作流程,拒绝策略,以及动态调整和监控的建议。同时,结合系统引用中的信息来增强答案的可信度,比如引用[3]和引用[5]中的具体建议。</think>### 线程池队列大小设置与参数配置详解 #### 1. **队列大小是否可手动设置** **可以手动设置**。线程池的队列大小属于核心配置参数,通过初始化时指定队列容量实现。例如在Java中,使用`LinkedBlocklingQueue`时可明确设置容量: ```java BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(100); // 队列容量100 ThreadPoolExecutor executor = new ThreadPoolExecutor( corePoolSize, maxPoolSize, keepAliveTime, TimeUnit.SECONDS, queue ); ``` 若未设置容量,默认值为`Integer.MAX_VALUE`,可能导致内存溢出风险[^4][^5]。 #### 2. **线程池参数配置方法** ##### (1) **核心参数分类** - **核心线程数(corePoolSize)**:常驻线程数量,即使空闲也不会销毁。 - **最大线程数(maxPoolSize)**:线程池允许创建的最大线程数。 - **队列容量(workQueue)**:任务排队等待的存储空间。 - **非核心线程存活时间(keepAliveTime)**:超出核心线程数的线程空闲时的存活时间。 - **拒绝策略(RejectedExecutionHandler)**:队列满且线程数达上限时的处理方式。 ##### (2) **配置原则** | 参数类型 | 计算密集型任务(如数据分析) | I/O密集型任务(如网络请求) | |----------------|------------------------------------------|---------------------------------------| | 核心线程数 | `CPU核数 + 1` | `CPU核数 * 2` [^3] | | 队列容量 | 较小(防止堆积) | 较大(容忍I/O等待) | | 最大线程数 | 与核心线程数一致 | 根据系统负载动态调整(如`CPU核数 * 4`)| ##### (3) **拒绝策略选择** | 策略类型 | 行为 | 适用场景 | |-------------------------|----------------------------------------------------------------------|------------------------------| | AbortPolicy | 直接抛出RejectedExecutionException | 需要快速失败验证的场景 | | CallerRunsPolicy | 由提交任务的线程直接执行任务 | 需要保证任务不丢失的场景 | | DiscardOldestPolicy | 丢弃队列中最旧的任务,尝试重新提交当前任务 | 允许部分数据延迟处理的场景 | | DiscardPolicy | 静默丢弃新提交的任务 | 容忍任务丢失的低优先级场景 | #### 3. **配置步骤示例** 以Java线程池为例: ```java // 1. 定义队列容量为200 BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(200); // 2. 设置核心参数:核心线程4,最大线程8,存活时间30秒 ThreadPoolExecutor executor = new ThreadPoolExecutor( 4, 8, 30, TimeUnit.SECONDS, queue, new CustomThreadFactory(), // 自定义线程命名 new ThreadPoolExecutor.CallerRunsPolicy() ); // 3. 监控调整(通过API动态修改) executor.setCorePoolSize(6); // 运行时调整核心线程数 ``` #### 4. **关键注意事项** 1. **内存控制**:队列容量需结合JVM内存设置,例如单个任务占用1MB内存时,1000容量的队列需预留1GB内存[^5]。 2. **动态调整**:通过`setCorePoolSize()`等方法实现运行时参数优化。 3. **监控工具**:使用Arthas、Prometheus等工具监控队列堆积情况与线程活跃度。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值