Oracle 官方并没有给出线程池 corePoolSize 的具体参考值,因为这个值的大小应该根据实际业务场景和系统资源情况来进行优化调整。不同的业务场景和系统资源状况可能需要不同的 corePoolSize 设置。
在《Java并发编程实战》一书中,作者 Brian Goetz 等人指出,线程池的规模应该根据任务类型和计算密集度来确定,对于 CPU 密集型任务,应该将核心线程数设置为处理器核心数加 1 或者 2;对于 I/O 密集型任务,可以适当增加核心线程数以利用空闲的 CPU 时间。
这个建议是基于以下考虑:对于 CPU 密集型任务,线程需要大量计算,因此需要足够多的 CPU 资源,而处理器核心数加 1 或者 2 的数量可以充分利用 CPU 资源,避免线程之间的竞争和阻塞;而对于 I/O 密集型任务,由于线程大部分时间都处于等待 I/O 操作的状态,因此可以适当增加核心线程数以利用空闲的 CPU 时间,从而提高系统效率。虽然这个建议并非官方标准,但在实际应用中已经得到广泛的认可和应用,并取得了不错的效果。
线程池的参数配置需要根据实际场景和需求进行选择,以下详细的说明:
1、corePoolSize:线程池的 corePoolSize 表示核心线程数,即在不发生任务队列满或者其他拒绝策略下,线程池保持的最小线程数。
corePoolSize 的配置需要根据具体情况进行调整,以满足业务需求和系统性能要求。在进行 corePoolSize 配置时,需要考虑以下因素:
1)任务类型和数量:不同类型的任务对线程池的需求不同。比如 I/O 密集型任务可以使用更多的线程以提高并行性,而计算密集型任务则需要更少的线程以避免 CPU 过载。任务数量多时,线程池的 corePoolSize 需要相应增加。
2)系统资源限制:线程池的 corePoolSize 需要根据系统的硬件资源进行配置,如 CPU 核心数、内存大小等。过大的 corePoolSize 可能会导致系统过度消耗资源,从而影响系统的稳定性和可靠性。
3)线程池负载:如果线程池的任务队列已经满了,但是还有大量的任务等待执行,此时如果 corePoolSize 设置的过小,就可能导致任务不能及时得到处理。此时需要调整 corePoolSize 的值,以确保足够的线程可用。
4)响应时间要求:如果业务需求对任务的响应速度要求较高,那么需要考虑增加 corePoolSize 的配置,以提高任务的并发处理能力。
Oracle 官方并没有给出线程池 corePoolSize 的具体参考值,因为这个值的大小应该根据实际业务场景和系统资源情况来进行优化调整。不同的业务场景和系统资源状况可能需要不同的 corePoolSize 设置。不过,在《Java并发编程实战》一书中给出了建议。在这本书中,作者 Brian Goetz 等人指出,线程池的规模应该根据任务类型和计算密集度来确定。
对于 CPU 密集型任务,应该将核心线程数设置为处理器核心数加 1 或者 2;
对于 I/O 密集型任务,可以适当增加核心线程数以利用空闲的 CPU 时间。具体大小可以根据实际情况进行调整,建议设置在 2 x N 左右,其中 N 是可用 CPU 核心数。
这个建议是基于以下考虑:对于 CPU 密集型任务,线程需要大量计算,因此需要足够多的 CPU 资源,而处理器核心数加 1 或者 2 的数量可以充分利用 CPU 资源,避免线程之间的竞争和阻塞;而对于 I/O 密集型任务,由于线程大部分时间都处于等待 I/O 操作的状态,因此可以适当增加核心线程数以利用空闲的 CPU 时间,从而提高系统效率。虽然这个建议并非官方标准,但在实际应用中已经得到广泛的认可和应用,并取得了不错的效果。
2、maximumPoolSize: 最大线程数,表示线程池中最多能够容纳的线程数量。
根据实际业务情况、系统资源和性能考虑来选择,一般建议不要过大,避免因过度创建线程而影响系统性能和稳定性。最大线程数应该合理设置,避免过度创建线程造成资源浪费,但又要保证能够满足高并发情况下的需求。
在《Java并发编程实战》一书中,关于线程池的 maximumPoolSize 参数,建议设置为可用 CPU 核心数的两倍加一。这个建议是基于以下考虑:对于 CPU 密集型任务,因为线程需要大量计算,所以应该将线程池规模设置得稍微小一些,以避免过多的线程竞争和阻塞。对于 I/O 密集型任务,线程大部分时间都处于等待 I/O 操作的状态,因此可以适当增大线程池规模,以利用空闲的 CPU 时间,提高系统效率。据此,如果可用 CPU 核心数为 N,则建议将 maximumPoolSize 设置为 2N+1。
当然,这只是一个参考值,真正的线程池参数设置需要根据实际业务场景和系统资源情况进行优化。例如,如果任务类型比较多样化或者任务运行时间比较长,可以适当调整线程池规模,以保证系统稳定性和高效性的前提下,尽可能地充分利用系统资源,提高任务处理能力和效率。
**3、keepAliveTime:**一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁;如果allowCoreThreadTimeOut被设置为true时,无论线程数多少,线程处于空闲状态超过一定时间就会被销毁掉。
在《Java并发编程实战》一书中,关于线程池的 keepAliveTime 参数,建议设置为数十秒或者数分钟。这个建议是基于以下考虑:
对于 CPU 密集型任务,因为线程需要大量计算,所以不宜让空闲线程持续存在,以避免浪费系统资源。因此,建议将 keepAliveTime 设置得较短(例如 10 秒),以便及时回收空闲线程。
对于 I/O 密集型任务,由于线程大部分时间都处于等待 I/O 操作的状态,因此可以适当增加 keepAliveTime,以利用空闲的 CPU 时间,提高系统效率。建议 keepAliveTime 的值在数十秒或者数分钟之间。需要注意的是,这些参考值只是一个起点,真正的线程池参数设置应该根据实际业务场景和系统资源情况来进行优化调整。
例如,如果任务类型比较多样化,可以将 keepAliveTime 设置得稍微长一些;如果任务运行时间比较长,也可以将 keepAliveTime 调整得更长一些,以减少不必要的线程创建和销毁操作。最终的目标是在保证系统稳定性和高效性的前提下,尽可能地充分利用系统资源,提高任务处理能力和效率。
4、workQueue: workQueue是线程池的任务队列,它用来缓存等待执行的任务。workQueue的值应该根据具体的业务场景和需求来设置,不同的队列类型有不同的特点和适用场合。
一般来说,workQueue的值可以分为有界队列和无界队列两种。有界队列可以限制任务队列的长度,避免内存溢出,但是也可能导致任务被拒绝或者阻塞。无界队列可以接受任意数量的任务,但是也可能导致内存占用过大或者性能下降。
如果任务是CPU密集型的,那么可以选择一个较小的队列,以避免CPU空闲或者上下文切换过多。
如果任务是IO密集型的,那么可以选择一个较大的队列,以提高线程利用率和吞吐量。
如果任务是有优先级或者依赖关系的,那么可以选择一个支持排序或者延迟执行的队列。
如果任务是异步的,那么可以选择一个支持回调或者通知的队列。
5、RejectedExecutionHandler 在《Java并发编程实战》一书中,关于线程池的 RejectedExecutionHandler 参数,建议根据业务需求和系统情况选择合适的策略。以下是一些常用的策略及其特点:
CallerRunsPolicy:将任务回退到调用者线程执行。这种策略可以避免任务丢失,并且不会导致队列溢出,但可能会影响调用线程的性能。
AbortPolicy:直接抛出 RejectedExecutionException 异常,以拒绝新的任务提交。这种策略可以保证系统稳定性,但会导致部分任务无法完成,因此需要根据实际情况做好异常处理和日志记录。
DiscardPolicy:直接丢弃新的任务,不予处理。这种策略适用于那些对任务结果要求不高的场景,但也可能会导致任务丢失,因此需要慎重使用。
DiscardOldestPolicy:丢弃队列中等待时间最长的任务,并尝试重新提交新的任务。这种策略可以保证队列不会溢出,但可能会导致一些任务被丢失或延迟执行。
需要注意的是,以上策略都是针对有界队列的情况下,如果使用无界队列,则只有 CallerRunsPolicy 策略和 AbortPolicy 策略是有效的,而 DiscardPolicy 和 DiscardOldestPolicy 策略则没有意义。
最终的选择应该根据实际需求和系统情况进行评估和测试,以确保选择的策略符合业务需求,并且能够保证系统稳定性和高效性。