通过图表和流程图来精简解析线程池的各种参数配置和基本的工作原理. 更基础的一些原理, 例如condition的等待和通知等会单独再开一篇讲解.
参数解析
理解了ThreadPoolExecutor每个构造参数的含义和作用后, 就可以自己根据需求定制类似newFixedThreadPool
, newSingleThreadExecutor
, newCachedThreadPool
这样的特定线程池.
参数名 | 描述 |
---|---|
corePoolSize | 核心线程数, 常驻线程, 除非设置了allowCoreThreadTimeOut |
maximumPoolSize | 最大线程数, maximumPoolSize - corePoolSize就是工作线程数 |
keepAliveTime | 工作线程idle 设定时间后, 会将闲置的工作线程回收, 如果设置了allowCoreThreadTimeOut, 核心线程也会被回收 |
TimeUnit | 线程回收需要等待闲置时间的单位 |
workQueue | 阻塞任务的排队队列 |
ThreadFactory | 线程池用来创建线程的工厂 |
handler | 线程池满了等原因造成添加任务失败的处理方案:AbortPolicy 默认策略直接抛异常.CallerRunsPolicy 在添加任务的线程里运行当前任务.DiscardPolicy 放弃执行当前的任务.DiscardOldestPolicy 放弃工作队列中排在头部的一个任务, 在添加当前任务. |
运行状态
状态名 | 描述 | 下级状态 |
---|---|---|
RUNNING | 正常运行, 可以提交新任务进来 | shutdown() -> SHUTDOWN. shutdownNown() -> STOP |
SHUTDOWN | 停止提交新任务, 正在运行和在队列中排队的任务继续执行完成 | TIDYING |
STOP | 停止提交新任务, queu也阻塞, 并interrupt正在执行的线程 | TIDYING |
TIDYING | 调用停止后, 活动任务数量为0时, 进入tidying状态. | TERMINATED |
TERMINATED | tidying后会尝试调用terminated方法进入 terminated状态 | - |
工作队列
缓存任务的排队容器, 根据不同的长度和特性有不同的使用场景.
队列名称 | 是否有界 | 描述 |
---|---|---|
SynchronousQueue | 是 | 0容量无缓冲队列, 有任务进来会直接给线程池去消费, 在消费的过程中会阻塞新任务进来. 当新任务不能进入消息队列时, 线程池就会创建新的工作线程. 所以最好搭配maximumPoolSize的最大设置Integer.MAX_VALUE. 在执行任务时被线程池拒绝异常 |
ArrayBlockingQueue | 是 | 固定设置容量的数组队列. 可以使用先入先出规则. |
LinkedBlockingQueue | 否 | 无限容量. 链表队列. 先入先出. |
PriorityBlockingQueue | 否 | 同LinkedBlockingQueue, 任务必须实现compare方法, 来对链表进行排序 |
execute时线程数量和任务队列的关系
ThreadPoolExecutor的执行流程如下图所示, 简单用三句话来描述:
- 核心线程数没满时, 就直接开核心线程.
- 核心线程数满了, 来新任务后就扔到队列中排队.
- 如果队列排满了, 再去开工作线程. 直到线程数达到最大线程数.
无边界队列在使用时需要注意, 因为一旦使用了无边界队列, 那么线程池的maximumPoolSize就会失效. 除了corePoolSize数量的核心线程或者唯一一个工作线程在运行外, 其他所有的任务都会在工作队列中排队.
创建线程并消费队列中任务
-
Worker
Worker是线程池中任务的消费者, 每一个Worker代表了一个线程. Worker本身是继承于AbstractQueuedSynchronizer, 在构造时创建一个线程作为自己的属性, 通过设置status 0 | 1来修改当前的线程独占状态. 如果有初始任务的话在启动线程后直接运行, 并记录自己完成任务的数量.
-
addWorker
线程池内部维护了一个workers数据集, 在内部访问workers时必须用一个全局可重入锁保证互斥. 创建worker的时机可以参考上一节的内容.
创建worker的细节如下图所示, 简单用三句话来描述:
- 如果当前线程池状态不允许再创建worker, 则直接失败.
- 创建worker后, 如果线程池和worker内部的线程状态都正常, 将worker添加进workers数据集并更新数量.
- 如果worker已经添加到workers中后, 开始运行worker内部的线程.
-
runWroker
创建worker成功时, 会开始运行新worker内部的线程. 线程启动后调用
runWorker(Worker)
方法, 在取任务时, 会根据不同的情况让线程处于等待状态.- 如果有初始任务或者能从阻塞队列中提取到任务, 就执行
- 根据运行状态重置或者设置线程的中断位
- 任务执行前后回调线程池子类实现的方法
- 如果执行任务异常或者循环阻塞获取任务为null, 则标记并清理当前worker
-
getTask
在worker运行时会循环从阻塞队列中获取任务来执行, 这一过程会根据不同的情况阻塞等待不同的时间.
- 当前状态不能获取任务时, 直接返回空任务, 结束当前worker.
- 如果当前超时设置生效, 则从任务队列中poll个任务出来. 如果队列为空则等待超时.
- 如果当前超时设置无效, 则从任务队列中take个任务出来. 如果队列为空则一直等待, 直到线程中断或接受到信号.
- 如果取任务时发生中断或者任务为空后从1开始循环.
- 返回有效的任务给worker运行.
转载请注明出处:http://blog.csdn.net/l2show/article/details/103480185