Java并发包中线程池 ThreadPoolExecutor 原理探究
介绍
线程池主要解决的两个问题: 一是当执行大量异步任务是线程池能提供较好的性能,使线程可复用而不需要再次new,二是提供一种资源限制和管理手段,比如可以限制线程的个数,动态新增线程等。
另外线程池也提供了许多可调参数和可扩展性接口,以满足不同情况的需要。我们可以使用更方便的 Executors 的工厂方法去创建线程池,比如其中的 newCachedThreadPool (线程池线程个数最多科大 Integet.MAX_VALUE 线程自动回收), newFixedThreadPool (固定大小)和 newSingleThreadExecutor (单个线程)。
类图介绍

ThreadPoolExecutor 继承了 AbstractExecutorService, 而 AbstractExecutorService 实现了 ExecutorService, ExecutorService 则实现了 Executor。 ThreadPoolExecutor 中有五个内部类,其中有四个是关于 policy 的,这是对于拒绝策略的实现(当线程池的状态不能再添加新任务执行的策略), 还有一个 Worker 类, 他继承了 AQS 和实现了 Runnable,里面封装了firstTask记录着第一次执行的任务,thread保存的线程,这个线程的 Runnable 是 Worker 本身,当初始化的时候会将 this 传入 thread ,当 thread 启动的时候会调用 Worker 实现的 run() 方法, 其中会调用runWorker(this),在这个方法里面就会取出 firstTask 然后调用它的 run 方法。
这里有一个 ctl 的原子变量, 他和前面的读写锁有着异曲同工之妙,它通过一个变量记录着线程池状态和线程池线程个数,其中高三位表示线程池状态,其余表示线程池个数
1 | // 初始化线程池状态为RUNNING 个数为0 |
线程池参数如下
corePoolSize: 线程池核心线程数。
workQueue: 用于保存等待执行的任务的阻塞队列,比如基于数组的有界ArrayBlockingQueue,基于链表的无界 LinkedBlockingQueue等等等。
maximunPooSize: 线程池最大线程数
threadFactory: 创建线程的工厂
RejectedExecutionHandler: 饱和拒绝策略 前面提到过
keepAliveTime: 存活时间。如果当前线程池中的线程数量比核心线程数量多的时候,并且是闲置状态,则这些闲置的线程能存活的最大时间。
TimeUnit: 存活时间的时间单位。
mainLock: 独占锁, 用来控制新增 Worker 线程操作的原子性 。
termination: mainLock对应的条件队列,在线程调用 awaitTermination 时被阻塞的线程放入此队列。
Worker: 上文提到过 是具体承载任务的对象,它继承了 AQS 实现了自己的简单不可重入独占锁,其中state = 0表示锁未被获取,为1表示锁已经被获取,为-1表示创建的默认状态,为了避免该线程在运行newWorker()方法前被中断。
源码分析
public void execute(Runnable command)
execute 方法的作用是提交任务 command 到线程池进行执行。

1 | public void execute(Runnable command) { |
工作线程worker的执行
当用户线程添加到线程池后,由worker来执行,先看下Worker的构造函数。
1 | Worker(Runnable firstTask) { |
shutdown 操作
调用shutdown方法后 线程池就不会接受新的任务了,但是工作队里里的任务还要执行的,该方法立即返回 不等待队列任务完成再返回。
1 | public void shutdown() { |
总结
线程池巧妙地使用了Integer类型表示线程池状态和线程数量,在整个线程池中主要的操作其实就是execute,当执行的时候会判断线程池状态 线程数量等因素。如果不满足那么会将任务(Runnable)加入阻塞队列 ,如果成功那么会加入工作集中并且执行。整个线程池能做到线程复用主要是吧Runnable和Thread分开来了,如果线程存活的时候且任务为空,当一个任务进来的时候该线程就能执行该任务。这样就减少了一些不必要的开销。当然还有很多线程池管理的细节,这里就不细说了。