并发笔记-ThreadPoolExecutor详解
简介
ThreadPoolExecutor一种ExecutorService,使用可能的多个池线程之一执行每个提交的任务,通常使用Executors工厂方法进行配置。
线程池解决了两个不同的问题:它们通常在执行大量异步任务时提供更好的性能,这是因为减少了每个任务的调用开销;它们还提供了一种方法来限制和管理执行任务集合时消耗的资源,包括线程。每个ThreadPoolExecutor还维护一些基本统计信息,例如已完成任务的数量。
为了在广泛的上下文中有用,这个类提供了许多可调整的参数和可扩展性挂钩。然而,我们敦促程序员使用更方便的Executors工厂方法Executors。newCachedThreadPool(无边界线程池,具有自动线程回收),执行器。newFixedThreadPool(固定大小的线程池)和执行器。newSingleThreadExecutor(单后台线程),为最常见的使用场景预配置设置。否则,在手动配置和调优此类时,请使用以下指南:
七大核心参数详解
- int corePoolSize,//线程池的核心线程数量
- int maximumPoolSize,//线程池的最大线程数
- long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
- TimeUnit unit,//时间单位
- BlockingQueue
workQueue,//任务队列,用来储存等待执行任务的队列 - ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
- RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
BlockingQueue
RejectedExecutionHandler
AbortPolicy(抛出异常)
AbortPolicy,缺省策略,处理程序在拒绝时抛出runtime RejectedExecutionException。
CallerRunsPolicy(自身执行)
CallerRunPolicy,调用自身执行的线程运行任务。这提供了一种简单的反馈控制机制,可以降低提交新任务的速度。
DiscardPolicy(丢弃当前)
DiscardPolicy,将简单地删除无法执行的任务。
DiscardOldestPolicy(丢弃队头)
DiscardOldestPolicy,如果未关闭执行器,将丢弃工作队列头部的任务,然后重试执行(可能再次失败,导致重复执行)
如何合理配置线程池参数
一般采用如下方式进行配置,但是这种只是理论,在实际的生产环境表现并不是那么令人满意,更加推荐后面两种方式
普通配置
1.CPU密集型
单核:当我们是单核CPU的时候,不建议使用线程池
多核:当我们是多核CPU的时候,理论上应该是 线程数=CPU核数,但实际上,一般会设置为 核数+1,这是因为万一某个线程因为某个未知的错误停止的时候,可以确保有一个“额外”线程可以执行任务,让CPU持续工作
简单来说:线程数=CPU核数+1
2.IO密集型
IO密集型与CPU密集型相对,一个完整的请求,CPU执行完后需要进行很多IO操作,也就是IO操作需要大量时间,却不占用CPU资源,所以理论上我们一般保证有多少个IO大型任务(n),线程数就是这个任务数的两倍(2n),但实际上,一般是2n+1个
线程数=2*CPU核数+1
结合任务配置
我们来分析一下哪些因素影响我们对线程池参数的配置
- 任务量
- 任务执行耗时
- 允许最大的响应时间
- 机器硬件大小(CPU核心数)
案例
1 |
|