1.核心数据结构
ThreadPoolExecutor
//线程池状态和运行起来的线程数量
private final AtomicInteger ctl;
// 存放任务的阻塞队列
private final BlockingQueue workQueue;
// 对线程池内部各种变量进行互斥访问控制
private final ReentrantLock mainLock;
// 线程集合
private final HashSet workers;
Worker
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
// ...
final Thread thread; // Worker封装的线程
Runnable firstTask; // Worker接收到的第1个任务,可以是null,如果是null,则线程从队列获取任务
volatile long completedTasks; // Worker执行完毕的任务个数
// ...
}
2.核心配置参数
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit,
BlockingQueue workQueue, ThreadFactory threadFactory)
- corePoolSize:在线程池中始终维护的线程个数。
- maxPoolSize:在corePooSize已满、队列也满的情况下,扩充线程至此值。
- keepAliveTime/TimeUnit:maxPoolSize 中的空闲线程,销毁所需要的时间,总线程数收缩
回corePoolSize。 - blockingQueue:线程池所用的队列类型。
- threadFactory:线程创建工厂,可以自定义,有默认值
Executors.defaultThreadFactory() 。 6. RejectedExecutionHandler:corePoolSize已满,队列已满maxPoolSize 已满,最后的拒绝策略。
3.线程池关闭
这里的状态迁移有一个非常关键的特征:从小到大迁移,-1,0,1,2,3,只会从小的状态值往大的状态值迁移,不会逆向迁移。
正确关闭线程池的步骤
关闭线程池的过程为:在调用 shutdown()或者shutdownNow()之后,线程池并不会立即关闭,接下来需要调用 awaitTermination() 来等待线程池关闭。
shutdown()与shutdownNow()的区别
1.shutdown()不会清空任务队列,会等所有任务执行完成,shutdownNow()清空任务队列。
2.shutdown()只会中断空闲的线程,shutdownNow()会中断所有线程。
4 任务的提交过程分析
- 如果当前线程数小于corePoolSize,则启动新线程。添加Worker,并将command设置为Worker线程的第一个任务开始执行。
- 如果当前的线程数大于或等于corePoolSize,则调用workQueue.offer放入队列。如果线程池正在停止,则将任务从队列移除,并拒绝任务请求。否则,放入队列中后发现没有线程执行任务,开启新线程
- 线程数大于maxPoolSize,并且队列已满,调用拒绝策略
5.任务的执行过程分析
Woker的run()方法的实现过程
- 使用给定的第一个任务并利用线程工厂创建Worker实例,调用Woker的run方法,再调用ThreadPoolExecutor的runWorker方法执行线程的运行
- runWorker:中断Worker封装的线程,此时任务线程就可以开始运行了。获取线程锁,执行task.run()方法,执行任务中的代码。w.completedTasks++,线程已完成的任务数加1
6.线程池的4种拒绝策略
workqueue用完之后在execute(Runnable command)的最后,调用了reject(command)执行拒绝策略
- 调用者直接在自己的线程里执行,线程池不处理,比如到医院打点滴,医院没地方了,到你家自己操作吧
- 线程池抛异常
- 线程池直接丢掉任务,神不知鬼不觉
- 删除队列中最早的任务,将当前任务入队列