核心和最大线程数(Core and maximum pool sizes)
ThreadPoolExecutor
会根据指定的 corePoolSize
和 maximunPoolSize
自动调整当前线程池内线程数量(通过 getPoolSize
可以获取当前线程数量)。
值得注意的是:当 corePoolSize
大于当前线程数,即使当前存在空闲的线程,ThreadPoolExecutor
也会创建一个新的线程来处理新加入的任务。
之后只有在当前线程数介于 corePoolSize
和 maximunPoolSize
之间并且队列满了之后,ThreadPoolExecutor
才会再创建新的线程。
一般情况下,核心和最大线程数会在构造线程池对象的时候指定,但是也可以通过 setCorePoolSize
和 setMaximunPoolSize
来动态指定。
预先启动线程
默认情况下,核心线程会在初始阶段就构建,但是在当新任务到达之后才会启动。
可以通过覆盖 prestartCoreThread
和 prestartAllCoreThreads
方法来改变默认行为来预先启动线程。
线程工厂
ThreadPoolExecutor
默认使用的线程工厂创建出的线程是:在同一个 ThreadGroup
中的,NORM_PRIORITY
优先级的,非守护(non-daemon status
)的。
线程存活时间
在当前线程数大于 corePoolSize
的时候,超过该数量的线程会在空闲状态超过指定的 keepAliveTime
时间后终止。
线程的存活时间可以通过 setKeepAliveTime
动态指定。
默认该存活规则只适用于超出核心线程数的额外线程,但可以通过 allowCoreThreadTimeOut
来应用至核心线程。
阻塞队列
情形 | 优先行为 |
---|---|
当前线程数 < 核心线程数 | 优先添加新线程 |
核心线程数 <= 当前线程数 <= 最大线程数 | 优先放置在队列中 |
核心线程数 <= 当前线程数 | 优先添加新线程 |
最大线程数 <= 核心线程数 | 执行拒接策略 |
拒绝策略
当线程池已经关闭 或者 任务数超过队列容量和最大线程数 时,会执行拒绝策略
预定义的拒绝策略有:
策略 | 行为 |
---|---|
ThreadPoolExecutor.AbortPolicy | 抛出 RejectedExecutionException 异常 |
ThreadPoolExecutor.CallerRunsPolicy | 调用 execute 的线程执行该任务(提供了一个被压策略,减缓任务的提交速率) |
ThreadPoolExecutor.DiscardPolicy | 丢弃任务新任务 |
ThreadPoolExecutor.DiscardOldestPolicy | 在线程池未关闭的情况下,丢弃队列头最老的任务,并再次尝试执行(可能执行失败,导致再次重复该策略) |
Hook方法
线程池任务执行相关的生命周期方法,适用于一些场景的功能增强:刷新ThreadLocal工具状态,聚合统计,日志等等
方法 | 解释 |
---|---|
beforeExecute | 执行任务之前 |
afterExecute | 执行任务之后 |
terminated | 线程池完全关闭之后 |
注意这些hook方法如果抛出异常可能导致线程池内部线程执行失败或突然终止。
维护队列
可以直接使用 getQueue
接口来对任务队列进行监控或者调试。
如果有大量任务在队列中,可以调用 remove
和 purge
接口来帮助回收存储空间
线程池回收
线程池在应用中不被引用 并且 线程池内也没有剩余线程,那么线程池会自动关闭。
该特性可以配合 allowCoreThreadTimeOut
使得在不显式调用 shutdowm
接口时,可以自动关闭,回收资源。