在 Java 中,ThreadPoolExecutor
是线程池的核心实现类,它提供了丰富的配置选项和拒绝策略,用于高效管理线程的执行。线程池的七个参数和拒绝策略是线程池工作机制的重要组成部分,下面将详细解释它们的作用。
1. 线程池的七个参数
ThreadPoolExecutor
的构造函数包含七个关键参数,分别为:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1.1 corePoolSize
(核心线程数)
- 作用:线程池中保持的核心线程数,即使在空闲时,线程池也会保存这么多线程,以备执行新的任务。
- 说明:当提交一个新任务时,如果当前运行的线程数少于核心线程数,线程池会创建新线程来处理任务,而不管当前的线程是否处于空闲状态。
1.2 maximumPoolSize
(最大线程数)
- 作用:线程池中允许创建的最大线程数。当任务队列已满且当前线程数少于
maximumPoolSize
时,线程池会继续创建新线程来处理任务。 - 说明:当任务队列满了且线程数达到
maximumPoolSize
后,新任务将被拒绝,触发拒绝策略。
1.3 keepAliveTime
(线程存活时间)
- 作用:当线程池中的线程数量大于
corePoolSize
时,多余的空闲线程在等待新任务时最多保持多长时间,然后终止。 - 说明:此参数仅在线程池中线程数超过
corePoolSize
时才会生效。当线程数大于核心线程数时,如果空闲线程的存活时间超过keepAliveTime
,则会被回收。
1.4 unit
(存活时间的时间单位)
- 作用:指定
keepAliveTime
参数的时间单位。TimeUnit
是一个枚举类,常见的取值有TimeUnit.SECONDS
、TimeUnit.MILLISECONDS
等。
1.5 workQueue
(任务队列)
- 作用:用来存放等待执行的任务的队列。任务在没有可用线程时,会先进入队列等待执行。
- 常见队列类型:
ArrayBlockingQueue
:一个基于数组的有界阻塞队列。LinkedBlockingQueue
:一个基于链表的无界阻塞队列(理论上是无界,但有实际内存限制)。SynchronousQueue
:一个不存储元素的队列,每个插入操作必须等到另一个线程调用移除操作,否则会阻塞。PriorityBlockingQueue
:具有优先级的无界队列,任务会按照优先级进行调度。
1.6 threadFactory
(线程工厂)
- 作用:用于创建新线程。通过
ThreadFactory
可以自定义线程的创建方式,比如为线程设置名称、设置为守护线程等。 - 默认实现:
Executors.defaultThreadFactory()
,它会创建非守护线程,优先级为Thread.NORM_PRIORITY
。
1.7 handler
(拒绝策略)
- 作用:当线程池的任务队列已满,且线程数达到了
maximumPoolSize
后,线程池会拒绝新的任务提交。此时的处理方式由拒绝策略决定。 - 常见的拒绝策略:
AbortPolicy
(默认策略):直接抛出RejectedExecutionException
异常,阻止系统正常运行。CallerRunsPolicy
:由调用线程处理该任务,也就是提交任务的线程会直接运行这个任务。DiscardPolicy
:直接丢弃任务,不抛出异常。DiscardOldestPolicy
:丢弃队列中最旧的任务(最早进入队列、最晚执行的任务),然后重新尝试执行当前任务。- 自定义策略:可以通过实现
RejectedExecutionHandler
接口来自定义拒绝策略。
2. 线程池的工作流程
- 任务提交:当向线程池提交一个任务时,如果当前线程数小于
corePoolSize
,线程池会立即创建一个新线程来执行任务。 - 队列等待:如果线程数已经达到
corePoolSize
,任务将被加入到workQueue
中排队等待。 - 新线程创建:当队列已满并且线程数小于
maximumPoolSize
时,线程池会创建新线程来执行任务。 - 任务拒绝:如果队列已满并且线程数达到了
maximumPoolSize
,线程池会根据指定的拒绝策略处理新任务。
3. 拒绝策略详解
3.1 AbortPolicy
(抛出异常)
- 描述:该策略会直接抛出
RejectedExecutionException
,终止任务的执行。 - 适用场景:适合对任务拒绝敏感的场景,保证任务提交失败时能够立即通知开发者进行异常处理。
3.2 CallerRunsPolicy
(调用者运行策略)
- 描述:该策略不会丢弃任务或抛出异常,而是让提交任务的线程直接执行该任务,减缓线程池的压力。
- 适用场景:适合不能丢任务,但线程池压力不大的情况。通过让调用线程执行任务,降低系统的并发量。
3.3 DiscardPolicy
(丢弃任务)
- 描述:该策略会直接丢弃不能处理的任务,不会抛出任何异常。
- 适用场景:适合对任务丢失不敏感的场景,比如日志处理、临时计算等非关键任务。
3.4 DiscardOldestPolicy
(丢弃最旧任务)
- 描述:该策略会丢弃队列中最旧的任务(队列头的任务),然后重新尝试提交当前任务。
- 适用场景:适合优先处理最新任务的场景,确保新任务能够被及时处理。
3.5 自定义拒绝策略
- 描述:开发者可以通过实现
RejectedExecutionHandler
接口来自定义拒绝策略。这样可以根据具体业务需求,灵活处理任务拒绝情况。
总结
ThreadPoolExecutor
提供了丰富的配置参数和拒绝策略,使得线程池能够在不同场景下灵活运作。合理设置线程池的核心线程数、最大线程数和任务队列,以及根据业务特点选择合适的拒绝策略,能够提高系统的性能和稳定性。