Skip to content
Go back

ThreadPoolExecutor的重要特性和概念

Updated:  at  04:35 PM

核心和最大线程数(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 接口时,可以自动关闭,回收资源。


Suggest Changes

Previous Post
Java中的字段拷贝复制
Next Post
现代 Git 特性的推荐命令方案