创建线程池是需要指定如下几个参数,如:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1、每个参数的含义及要点:
参数名 | 含义 | 注意要点 |
---|---|---|
corePoolSize | 核心线程数,线程池中维护的最小线程数量(包含活跃和空闲的线程数量) | 1、线程池创建后,不是默认就会创建corePoolSize大小的线程数,而是有任务提交了才会开始创建线程 2、线程池中维持着corePoolSize大小的线程数,包含活跃和空闲线程 3、默认情况核心线程数即使为空闲的状态,keepAliveTime也不会生效,线程池中始终有corePoolSize大小数量的线程数 4、如果设置了allowCoreThreadTimeOut = true,那么核心线程也会在keepAliveTime时间到达后销毁,线程池中维护的线程数可能为0 |
maximumPoolSize | 最大线程数,线程池允许创建的最大线程数量 | 1、maximumPoolSize 一般大于corePoolSize的值 2、如果maximumPoolSize = corePoolSize,那么该线程池是固定大小的线程池 |
keepAliveTime | 线程池中空闲的线程保活时间 | 1、默认情况下(allowCoreThreadTimeOut = false),只有当线程池中的运行线程数量超过了corePoolSize时,keepAliveTime才会起作用,即keepAliveTime只对超出corePoolSize部分的线程起作用(举例说明:corePoolSize = 10,当前有15个线程在运行,此时运行线程超过了核心线程数,当线程执行完任务之后处于空闲时,当时间达到keepAliveTime的临界值后,有5个线程会被销毁,线程池中最后只会有10个可用的线程数) 2、如果线程池设置了allowCoreThreadTimeOut = true,那么keepAliveTime同样对核心线程启作用,即核心线程中线程空闲下来后,也会在时间达到keepAliveTime的临界值后销毁,线程池中的线程数可以为0 |
unit | 保活时间单位(例如:秒,毫秒等) | TimeUnit.DAYS; // 天 TimeUnit.HOURS;// 小时 TimeUnit.MINUTES; // 分 TimeUnit.SECONDS;// 秒 TimeUnit.MILLISECONDS;// 毫秒 TimeUnit.MICROSECONDS;// 微秒 TimeUnit.NANOSECONDS;// 纳秒 |
workQueue | 任务阻塞队列,当任务数超过当前线程池中的核心线程数量时,切队列未满,需要执行的任务会优先被放入到阻塞队列中进行等待 | 参考3 |
threadFactory | 线程工厂对象,用来创建新的线程实例 | 可自定义 |
handler | 拒绝执行处理策略 | 参考4 |
2、线程增长过程讲解:
corePoolSize、maximumPoolSize、workQueue:
ThreadPoolExecutor会根据这几个参数的边界内自动调整线程池中线程数量的大小,当执行execute(java.lang.Runnable)方法提交新任务时:
运行线程数(current) < corePoolSize 时
即使线程池中有空闲的线程,也会创建新的线程来执行新提交的任务
运行线程数(current) >= corePoolSize 时,此时说明核心线程数全部都处于繁忙状态中,新提交任务后,可能存在如下几种情况:
如果workQueue阻塞队列未满,则新的任务会被放入到队列中去等待空闲线程来进行执行,而不会创建新的线程
如果workQueue阻塞队列已满,且运行线程数 < maximumPoolSize 时,则会创建新的线程来执行新的任务
如果workQueue阻塞队列已满,且运行线程数 >= maximumPoolSize 时,则会通过handler策略来处理新的任务(handler策略方针后面会介绍)
任务处理的优先级(重复总结上面的规则,这个规则非常重要):
优先使用corePooSize范围内的线程
当核心线程都处于繁忙状态时,且阻塞队列未满,任务会被放入到阻塞队列中,而不是创建新线程来执行任务,等核心线程有空闲时,空线程会从阻塞队列中取出任务去执行
当阻塞队列已满,且当前运行线程数再运行的最大线程数范围内,则会创建新的线程来执行任务
当阻塞队列已满,且当前运行线程数达到线程池所运行的最大线程范围时,新的任务提交会被拒绝处理,拒绝处理的策略有多种方式,可以选择和自定义
3、使用的阻塞队列:
BlockingQueue 类型 | 特性及使用场景 |
---|---|
ArrayBlockingQueue | 有界队列,先进先出(FIFO)类型,需要指定队列大小,如果队列满了,会触发线程池的RejectedExecutionHandler拒绝处理策略 |
LinkedBlockingQueue | 无界队列,FIFO类型,可以无限向队列中添加任务,直到内存溢出 |
SynchronousQueue | 无界队列,一种特殊的阻塞队列,其中每个put操作(生产)必须等待一个take操作(消费),反之亦然。可以理解为没有容量的无界队列 |
PriorityBlockingQueue | 优先级队列,线程池会优先选取优先级高的任务进行执行,队列中的任务必须实现Comparable接口(该队列使用极少) |
4、拒绝处理策略方针:
拒绝处理策略通常是针对线程池中的有界队列而言的,无界对列,不存在队列已满的情况;当有界队列达到临界值后,新任务需要处理时会有问题,针对这种问题,线程池提供了以下几种处理策略:
RejectedExecutionHandler 类型 | 特性及效果 |
---|---|
CallerRunsPolicy | 如果任务添加失败,且线程池未关闭的情况下,那么调用线程(主线程)自己会调用执行器中的execute()方法来执行该任务 |
AbortPolicy | 线程池默认的策略方针,如果有任务添加失败,则会丢弃掉该任务,并抛出RejectedExecutionException异常 |
DiscardPolicy | 如果任务添加失败,则会丢弃掉该任务,且不抛出任何异常 |
DiscardOldestPolicy | 如果任务添加失败,会将队列中最早入队的任务移除poll(),再尝试添加,如果还失败,则会按此策略不断的重试 |
自定义策略方针 | 如果以上的策略方针都不能满足要求,那么可以自定义符合场景的policy;实现RejectedExecutorHandler接口中的rejectedExecution()方法即可 |